-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parse question and answer from cell metadata #2
Comments
Shouldn't be too hard to implement. I will look into implementing this soon |
Actually, I can't see how to access cell metadata either from Python or from Javascript. It does not seem to be added to the DOM, for instance. Let me know if you have any ideas. As a less attractive workaround, I could add an option to take in the data as Base64-encoded JSON, which is easy to create but not human readable. Let me know what you think. |
Yes, I'd suddenly started wondering too about how the introspection would work. It's easy enough to access cell metadata from eg a notebook extension, but that is changing the scope a bit. From a quick search around, there's a recipe at https://habr.com/en/post/439570/ that shows how to set cell tags using magic, so presumably a complementary approach along the lines of |
Hmmm... I wonder if that js / magic approach might be limited where it works. Eg would it work in a notebook edited / run in VS Code? |
Okay, so this is very hacky and riffs on https://habr.com/en/post/439570/ with added an In a code cell, run: %%javascript
define('getCellMetadata', function() {
return function(element) {
var cell_element = element.parents('.cell');
var index = Jupyter.notebook.get_cell_elements().index(cell_element);
var cell = Jupyter.notebook.get_cell(index);
var jsonString = JSON.stringify(cell.metadata);
IPython.notebook.kernel.execute("cell_metadata = '"+jsonString+"'");
}
}); Alternatively, that can probably just be wrapped in Python code, eg as: display(Javascript("""
define('getCellMetadata', function() {
return function(element) {
var cell_element = element.parents('.cell');
var index = Jupyter.notebook.get_cell_elements().index(cell_element);
var cell = Jupyter.notebook.get_cell(index);
var jsonString = JSON.stringify(cell.metadata);
IPython.notebook.kernel.execute("my_out = '"+jsonString+"'");
}
});
""")) Then define some magic: def _get_metadata(key):
display(Javascript(
"""
require(['getCellMetadata'], function(getCellMetadata) {
getCellMetadata(element);
});
"""
))
@register_line_cell_magic
def get_metadata(line, cell=None):
_get_metadata(line) Then in a code cell that has metadata attached, run the magic: %get_metadata Then in another code cell you can reference the stringified metadata: cell_metadata I think this needs to be accessed in a cell separate to line magic cell because of the async way in which things get executed? |
"Jupyter" isn't even defined in JupyterLab. I can't find anything equivalent, and I listed out all the local objects in JS. |
Ah... I still haven't moved to JuptyerLab/RetroLab: too complicated for me. I'm pretty much locked into classic notebook. |
If you write an extension, then you can get access to cell metadata (for example, the JupyterLab A more elaborate way of making use of an extension might be an extension that supports authoring of example answer tests, cf. cell tests as per the |
TLDR: I plan to close this issue after adding base64 encoding. Using this plus Collapse Selected Code in JupyterLab seems a good compromise in terms of implementing the behavior you want. An extension is out of my expertise and interest on this project. I have put some time into trying to find a way to access the cell metadata from either cell JavaScript or Python, and unfortunately I haven't figured out how it can be done in JupyterLab. If you have any knowledge about how to do that, let me know. I will go ahead and add the ability to read in the questions as base64-encoded stringified JSON as a stopgap. In JupyterLab, you can then set the cell containing that to be "hidden" in JupyterLab via View-> Collapse Selected Code. It can be "uncollapsed", but the questions/answers won't be human readable. I don't know if Collapse carries over to Jupyter notebook. I did some experiments at trying to auto-hide things using JS, but basically all that hiding would go away when the document was reloaded, unless the JS cell was explicitly run. |
Okay, thanks for exploring this too. I think that there are various discussions around extending the notebook format, as well as proposals regarding the propagation of metadata to kernels in JupyterLab, but actually contributing to those is outside my skillset; I will however maintain a watching brief over them until such a time as they become available and can be easily coopted for use... |
I think I have implemented nearly equivalent behavior to satisfy this request. You can now store a question as either JSON or base64-encoded JSON in a hidden HTML element within a Markdown cell. Then you can show that question using JupyterQuiz by passing the ID of the HTML element. This works in both Jupyter Notebook and Jupyter Lab. Please see the notebook HideQuiz.ipynb for examples. I will update the README.md later to document this feature. I hope that you find this helpful! |
I am reopening this issue since I did make some progress in implementing it and want this open until I get comments back from @psychemedia |
Ah, that's neat... To obfuscate a little bit more, I guess the markdown cell with the hidden answer could also be placed anywhere in the notebook. Though to help the author, a conventional location, such as the end of the notebook, might be most convenient. (TBH, I'm pretty casual about students looking up answers to formative questions, so as long as there is some friction to peaking at the answers, I suspect most won't (or won't know how to!). And whilst it's their loss if they do, it does demonstrate other skills if they know how to decode answers by technical means...!) To simplify authoring, I guess a way to generate the "release" notebook could be to tag answer cells (containing explicit plaintext answers) with a particular tag and then run the notebook through a simple offline processor to rewrite the cell contents as base64 encoded elements? Another approach would be a simple extension to do that (I reckon I could do that easily enough in classic notebook, but I still haven't got myself into a position where I can write JupyterLab extensions...) I guess there is a security implication in that the b64 could hide nasty js that could be evaluated (I'm not very well versed in security / sanitising operations, but I've started trying to at least look for things that a security audit might question). |
Thanks. Great comments. An authoring tool supporting rewriting the source data to base64 would be great. I'll add it as a to-do. I haven't worried too much about the security implications because of my particular use case, but I see what you are getting at. However, I guess there is little difference from when the JSON is loaded over the network -- the user doesn't get to see the JSON before it is loaded. I guess the cleanest way to resolve this would be to use something like GPB that would enforce a strict grammar. The downside is that we lose the lightweight, extendible nature of JSON. Looking on line, it seems that by using JSON.parse() instead of eval(), I will greatly reduce the security threats, so I will do that shortly. |
@psychemedia OK, I replaced those eval()s with JSON.parse(), so that should be much safer. However, I'm not a JS security expert by any means! Also, I completely updated HideQuiz.ipynb to provide a more detailed walkthrough for users. Please let me know if you have any comments or catch any bugs! |
Closing this because similar functionality has been implemented |
When authoring a self-contained notebook for rendering as a Jupyter Book, it's presumably easy enough to put question and answer definitions into a removed code cell in the final book output.
However, if the notebook is used as a notebook, the answers are evident in the code cell.
In such a case, it might be useful to pop the question and answer dictionary into the cell metadata as a JSON object and then render that into the multiple choice widget?
It's a bit of a faff for the notebook author having to edit the metadata cell, but it would keep the notebook self-contained and the answer source hidden to some extent.
The text was updated successfully, but these errors were encountered: