J

jupyter_renderjs_extension

Forked from Kreisel

Overview: RenderJS for Jupyter Notebook via nbextension

This repo contains two things:

  1. A jupyter frontend extension also called nbextension containing the javascript and html required to load arbitrary gadgets. See directory renderjs_nbextension.
  2. An ipython extension providing necessary python commands to initiate renderjs and control the loaded gadgets. This happens directly from the notebook (cells). See directory renderjs_ipyextension.

The extension needs both the nbextension and the ipython-extension installed and enabled in order to function.

Right now the ipython extension works with the Python 2 Kernel (tested) and should also work with the Python 3 Kernel (not tested).
In order to use the extension with the ERP5Kernel, the renderjs_ipyextension cannot be used. Instead the ERP5-backend provides the functionality directly.

Install

Via slapos

Nothing needs to be done, as the extension is shipped with the ipython-notebook software.

Manual

The whole directoy renderjs_nbextension/renderjs_nbextension needs to be installed and enabled as jupyter nbextension. Either use $ jupyter nbextension [install/enable], for instance

$ jupyter nbextension install path/to/jupyter_renderjs_extension/renderjs_nbextension/renderjs_nbextension --user --symlink
$ jupyter nbextension enable renderjs_nbextension/main --user

or copy/symlink manually to the appropriate jupyter directories. Further, copy the file renderjs_ipyextension/renderjs_ipyextension/renderjs_extension.py into the ipython directory (usually ~/.ipython for local install). Note, that the file must be placed directly into the ipython directory and cannot be contained in a directory.

Usage

All of the following python calls are made from the notebook itself. Note that there is a difference between the normal Python 2 Kernel and the ERP5Kernel regarding loading the extension.

Python 2 Kernel: Loading the ipython extension:

%load_ext renderjs_extension
import renderjs_extension as rjs

ERP5Kernel: Loading the ipython extension:

def rjs_setup():
    rjs = context.Base_loadRenderJSExtension()
    return { "rjs": rjs, }
environment.define(rjs_setup, 'rjs_setup')

The rest of the functionality is the same from bother Kernels.

Initialize the extension:

rjs.initRenderJS()

Load a renderjs gadget:

rjs.loadGadget("my_gadget_reference", "https://my.gadget.url/gadget.html")

Note, that you need to assign a unique reference for each gadget. This reference is used when calling declared methods and destroying the gadget.

Destroy a renderjs gadget:

rjs.destroyGadget("my_gadget_reference")

Call a declared_method of a loaded gadget:

arg1 = "Hello"
arg2 = 'World'
arg3 = 2017
arg4 = { "yes": "no!" }
rjs.callDeclaredMethod("my_gadget_reference", "method_name", arg1, arg2, arg3, arg4)

The number of arguments can is arbitrary as long as it is python-compatible. They get converted to js-arguments via pythons json.dumps. Keep that in mind when passing more complex arguments.

Development Notes

The extension is split into an ipython extension and a nbextension für jupyter. This is necessary because both JS in the frontend and python in the backend (client-Kernel/ERP5-backend) are required to use renderjs gadgets.

ipython extension (Python 2 Kernel, client-backend)

Here all the functionality for interacting with the extension via jupyter notebook cells is exposed. Unfortunately, the ipython environment has no knowledge of the kernel and therefore no knowledge of the jupyter frontend.
Still we want to execute javascipt in the frontend. To solve this issue, ipython is used to inject inline javascipt into the page via ipython display. This is a rather uncomfortable but there seems to be no better solution as of now. The injected JS code gets executed and then fires an event to which the frontend-extension listens to.

ERP5Kernel and erp5-server-backend

With the ERP5Kernel all the code of executed cells gets relayed to an actual ERP5-backend and is processed there. Thus no client-ipython-extension can be used. Instead a server-side script is in place to respond with text/html which contains the inline-javascript code.

nbextension (Frontend)

Nothing happens on-load of the frontend part. Everything is controlled by the backend (mostly via events fired from ipython). Exception to event-based communication is the initial and controller gadget (loading_gadget) which is injected into the page directly and which is initialized by a manual bootstrap of renderjs (since the page is already loaded; normal bootstrapping happens on-load).

The loading_gadget is responsible for loading other (actual) gadgets and pass information from the backend to them.
The loading_gadget declares a renderjs service for each possible event which could be fired from the backend (such as callDeclaredMethod, destroyGadget, ...).

Multiple gadgets are possible but all are controlled by the same loading_gadget. To differentiate between gadgets each gadget gets assigned a unique reference and the loading_gadget determines the correct gadget (by .getDeclaredGadget(ref)) and passes along the function call.