0

Jupyter is a web app which takes Python and other code types as input and executes them server-side (yes, there's JupyterLite which uses WebAssembly and Cython to execute it on the front end but it's irrelevant to this question), showing the results in the web app.

Let's say I had a good reason to want to change input values in one Python code cell based on something which happens in another code cell. Not change the value of a variable, but rather actually change a hardcoded value.

IPython (used in the Jupyter toolset) allows us to execute some JavaScript in our page via Python. So let's say the code setup look like this:

Code Cell 1:

def func(test=True, test2='hi', mitre=['T1059.001', 'T1027'], test3=['T1059.001', 'T1027']):
    pass

Code Cell 2:

# Now let's say some condition has happened, maybe the user has interacted with a
# GUI IPython widget to change the application state. But we want this state to be 
# saved if the user simply saves the Jupyter Notebook, which means we need to
# reflectively change the original values defined in in Code Cell 1, so the user
# doesn't need to remember to do so.

# In this case let's say we want to do something really simple, like replace 'T1027'
# with 'T1028' in the Code Cell 1 `mitre` argument.

from IPython.display import Javascript
Javascript('''
function replaceCode(value, replacement){
    const codeCells = document.body.getElementsByClassName('jp-CodeCell')
    for (const cell in codeCells) {
        if (Object.prototype.hasOwnProperty.call(codeCells, cell)) {
            const inputElem = codeCells[cell].getElementsByClassName('jp-Cell-inputWrapper')[0]
            const codeLines = inputElem.querySelector('[role="presentation"]').getElementsByClassName('CodeMirror-line')
            for (const line in codeLines) {
                if (Object.prototype.hasOwnProperty.call(codeLines, line)) {
                    const codeLineSegments = codeLines[line].querySelector('[role="presentation"]');
                    Array.from(codeLineSegments.children).forEach(segment => {
                            if (segment.innerHTML == value) segment.innerHTML = replacement
                    })
                }
            }
        }
    }
}

replaceCode("'T1027'", "'T1028'")
''')

This code actually runs successfully and works. The values are changed. The issue is that when I click the code-cell again, it reverts to the old state. Clearly, I need to make the elements update as if they had been focused and experienced keypresses, but the normal .click() methods and the solution I found here did not work: Is it possible to simulate key press events programmatically?

Maybe there's an easier way I'm missing, or some good solution here?

J.Todd
  • 707
  • 1
  • 12
  • 34
  • Not sure why you aren't making this an application or Dashboard using Voila (see [examples here](https://voila-gallery.org/)) so you have your Jupyter notebook structure & then you can use code to handle anything you want, albeit Python code, & the user doesn't see that part. Also consider if this direction is a long-term workable solution. I strongly suspect anything you build with javascript the way you show here looks to be built on brittle old notebook stuff that won't work with the newer version of the document-focused Jupyter notebook that will be built on the machinery like JupyterLab. – Wayne Oct 12 '22 at 15:10
  • This sentence [here](https://discourse.jupyter.org/t/location-of-libraries-or-extensions-installed-in-jupyterlab/16303/2?u=fomightez) is probably applicable to my concern about the whether that solution will work for very long: "jupyterlab is designed to be less 'hot swappable' in a running app than classic notebook, and doesn’t have a `require.js-equivalent`...". I took it as a better way to say what I've observed, and that is JupyterLab is way less flexible in the tricks people have found to use javascript and css to manipulate the classic Jupyter Notebook in the past. – Wayne Oct 12 '22 at 15:15
  • Getting back to what you are describing, it seems you want the user to have a notebook to use in the end or later as a way to record the values. I could see the app/Dasboard as making a Jupyter Notebook that they could use later if they had use the app/Dashbaord. None of that would involve coding javascript directly as I see it. You'd use nbformat and/or jupytex to make a notebook they could download. – Wayne Oct 12 '22 at 15:20
  • @Wayne I agree about the brittleness. I decided not to use this approach because of that. Instead, I'll store certain states in a separate JSON file to be stored locally with the notebook. But your critiques / thoughts make me think you might have good insight about the overall (FOSS) product I'm working on. Would you care to take a look and share thoughts? https://github.com/jt0dd/share/blob/main/diag-gui.drawio.svg – J.Todd Oct 12 '22 at 17:20
  • @Wayne Just noticed the SVG exports badly. Here's a PNG: https://github.com/jt0dd/share/blob/main/diag-gui.drawio.png – J.Todd Oct 12 '22 at 18:33
  • Looks look a great approach. Especially the use of notebooks to aid in continuity. If you didn't come across it already, nbformat is very useful for handling the elements of a notebooks as abstractions & making the json that actually underlies notebooks. – Wayne Oct 12 '22 at 19:00
  • 1
    @Wayne I didn't know about it. Thanks a ton. – J.Todd Oct 12 '22 at 19:19

0 Answers0