Jupyter tools

Tools to enable Node-Red/Jupyter interface

This flow is an example of the usage of 4 subflows:

  1. Get Servers: simply return a list of running Jupyter servers. Used internally.
  2. Jupyter Manager: Used to start, stop or reset Jupyter servers and kernels/sessions
  3. Execute python code: Send a block (can be quite complex, no problem) of python code to be executed on the running session
  4. Get Data from Python: Get standard variable/object from python. It needs to be serializable. An array of multiple variables is supported (used in the example). Lists, dicts, numpy arrays and pandas dataframes are supported.

Requires: @jupyterlab/services

On the python side, the example requires numpy and matplotlib.

[{"id":"30cc2af1b6f3f4e2","type":"subflow","name":"Get Data from Python","info":"Get data from a variable that exists in the Jupyter kernel.\r\n\r\nThe input payload is the variable name, or an array with the variable names (as strings).\r\nIf the requested variable(s) is(are) not defined, an empty string will be returned instead.\r\nAlso, if the requested variable is actually a non-serializable object, an empty string will be returned as well.\r\n\r\nCurrently, it returns a JSON containing the variables names and data as key/value pairs.\r\nSo far, this approach seems the most effective, as it can handle strings, numbers, arrays and etc. with ease.\r\n\r\nLists, tuples, dicts, numpy arrays and pandas dataframes are supported (nested or not).","category":"Jupyter","in":[{"x":80,"y":380,"wires":[{"id":"af3a7c35043180ad"}]}],"out":[{"x":1100,"y":380,"wires":[{"id":"04f1e343d60b2eb2","port":0},{"id":"7667d6b5b54a77c9","port":0}]}],"env":[],"meta":{"module":"node-red-jupyter-tools","version":"0.1","author":"Paulo Felipe Jarschel <[email protected]>","desc":"Get data from a variable that exists in the Jupyter kernel","keywords":"jupyter, python","license":"GPL-3.0"},"color":"#D8BFD8","icon":"font-awesome/fa-clipboard"},{"id":"c5b4ae7248df176f","type":"template","z":"30cc2af1b6f3f4e2","name":"Request Variable","field":"payload","fieldType":"msg","format":"python","syntax":"mustache","template":"import json\n\nvar_name = \"{{{payload}}}\"\nvar = \"\"\n\n# Try importing numpy and pandas\nnpok = False\npdok = False\ntry:\n    import numpy as np\n    npok = True\nexcept:\n    pass\ntry:\n    import pandas as pd\n    pdok = True\nexcept:\n    pass\n    \n\n# Functions\n# If it is a numpy array, convert it to list\ndef convert_np(var):\n    # If numpy is not installed, it does nothing\n    changed = False\n    if npok:\n        if isinstance(var, np.ndarray):\n            var = var.tolist()\n            changed = True\n    return var, changed\n\n# If it is a pandas dataframe, convert it to a json string\ndef convert_df(var):\n    # If pandas is not installed, it does nothing\n    changed = False\n    if pdok:\n        if isinstance(var, pd.DataFrame):\n            var = var.to_json()\n            changed = True\n    return var, changed\n\n# If it is a dict, list or tuple, recursively try to find numpy arrays and pandas dataframes to convert\ndef walk_list_convert(var):\n    changed = False\n    if isinstance(var, list) or isinstance(var, tuple):\n        for i in range(len(var)):\n            it = var[i]\n            newit, it_changed = check_convert_all(it)\n            if it_changed:\n                var[i] = newit\n                changed = True\n    return var, changed\n            \ndef walk_dict_convert(var):\n    changed = False\n    if isinstance(var, dict):\n        for key in var:\n            it = var[key]\n            newit, it_changed = check_convert_all(it)\n            if it_changed:\n                var[key] = newit\n                changed = True\n    return var, changed\n\n# Function to check all conversion possibilities\ndef check_convert_all(var):\n    changed = False \n    var, changed = convert_np(var)\n    if not changed:\n        var, changed = convert_df(var)\n    if not changed:\n        var, changed = walk_list_convert(var)\n    if not changed:\n        var, changed = walk_dict_convert(var)\n    \n    return var, changed\n\n\n# Try to find variable\ntry:\n    var = eval(var_name)\nexcept:\n    pass\n\n# Check is object is numpy array or pandas dataframe\nvar, changed = check_convert_all(var)\n\n# Quietly drop non-serializable objects\ntry:\n    print(json.dumps({var_name: var}))\nexcept:\n    print(json.dumps({var_name: \"\"}))","output":"str","x":330,"y":500,"wires":[["8d8e840fa1598939"]]},{"id":"04f1e343d60b2eb2","type":"function","z":"30cc2af1b6f3f4e2","name":"Convert to JSON","func":"msg.payload = JSON.parse(msg.payload);\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":500,"wires":[[]]},{"id":"8d8e840fa1598939","type":"subflow:0dcc892460200b20","z":"30cc2af1b6f3f4e2","name":"","x":560,"y":500,"wires":[["04f1e343d60b2eb2"]]},{"id":"878056838036a20a","type":"split","z":"30cc2af1b6f3f4e2","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":210,"y":160,"wires":[["2d1b21ca48c7e0ea"]]},{"id":"af3a7c35043180ad","type":"switch","z":"30cc2af1b6f3f4e2","name":"","property":"payload","propertyType":"msg","rules":[{"t":"istype","v":"array","vt":"array"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":190,"y":380,"wires":[["878056838036a20a"],["c5b4ae7248df176f"]]},{"id":"7667d6b5b54a77c9","type":"join","z":"30cc2af1b6f3f4e2","name":"","mode":"custom","build":"merged","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":970,"y":260,"wires":[[]]},{"id":"2d1b21ca48c7e0ea","type":"template","z":"30cc2af1b6f3f4e2","name":"Request Variable","field":"payload","fieldType":"msg","format":"python","syntax":"mustache","template":"import json\n\nvar_name = \"{{{payload}}}\"\nvar = \"\"\n\n# Try importing numpy and pandas\nnpok = False\npdok = False\ntry:\n    import numpy as np\n    npok = True\nexcept:\n    pass\ntry:\n    import pandas as pd\n    pdok = True\nexcept:\n    pass\n    \n\n# Functions\n# If it is a numpy array, convert it to list\ndef convert_np(var):\n    # If numpy is not installed, it does nothing\n    changed = False\n    if npok:\n        if isinstance(var, np.ndarray):\n            var = var.tolist()\n            changed = True\n    return var, changed\n\n# If it is a pandas dataframe, convert it to a json string\ndef convert_df(var):\n    # If pandas is not installed, it does nothing\n    changed = False\n    if pdok:\n        if isinstance(var, pd.DataFrame):\n            var = var.to_json()\n            changed = True\n    return var, changed\n\n# If it is a dict, list or tuple, recursively try to find numpy arrays and pandas dataframes to convert\ndef walk_list_convert(var):\n    changed = False\n    if isinstance(var, list) or isinstance(var, tuple):\n        for i in range(len(var)):\n            it = var[i]\n            newit, it_changed = check_convert_all(it)\n            if it_changed:\n                var[i] = newit\n                changed = True\n    return var, changed\n            \ndef walk_dict_convert(var):\n    changed = False\n    if isinstance(var, dict):\n        for key in var:\n            it = var[key]\n            newit, it_changed = check_convert_all(it)\n            if it_changed:\n                var[key] = newit\n                changed = True\n    return var, changed\n\n# Function to check all conversion possibilities\ndef check_convert_all(var):\n    changed = False \n    var, changed = convert_np(var)\n    if not changed:\n        var, changed = convert_df(var)\n    if not changed:\n        var, changed = walk_list_convert(var)\n    if not changed:\n        var, changed = walk_dict_convert(var)\n    \n    return var, changed\n\n\n# Try to find variable\ntry:\n    var = eval(var_name)\nexcept:\n    pass\n\n# Check is object is numpy array or pandas dataframe\nvar, changed = check_convert_all(var)\n\n# Quietly drop non-serializable objects\ntry:\n    print(json.dumps({var_name: var}))\nexcept:\n    print(json.dumps({var_name: \"\"}))","output":"str","x":330,"y":260,"wires":[["aaa84ec9833e67e1"]]},{"id":"53a6adacf88cb9db","type":"function","z":"30cc2af1b6f3f4e2","name":"Convert to JSON","func":"msg.payload = JSON.parse(msg.payload);\nif (msg.parts.index == msg.parts.count - 1) {\n    msg.complete = true;\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":260,"wires":[["7667d6b5b54a77c9"]]},{"id":"aaa84ec9833e67e1","type":"subflow:0dcc892460200b20","z":"30cc2af1b6f3f4e2","name":"","x":560,"y":260,"wires":[["53a6adacf88cb9db"]]},{"id":"0dcc892460200b20","type":"subflow","name":"Execute Python Code","info":"Takes a text input and attempt to run it at a Jupyter server.\r\nMultiline supported.\r\nEntire thousands-of-lines codes are supported.\r\n\r\nThe use of a template node as input is recommended, but anything goes.\r\n\r\nPython output is passed on as the msg.payload and logged (cropped) to the console.","category":"Jupyter","in":[{"x":60,"y":80,"wires":[{"id":"2c77f2ec0596b24f"}]}],"out":[{"x":340,"y":80,"wires":[{"id":"2c77f2ec0596b24f","port":0}]}],"env":[],"meta":{"module":"node-red-jupyter-tools","version":"0.1","author":"Paulo Felipe Jarschel <[email protected]>","desc":"Execute python code on a running Jupyter server","keywords":"jupyter, python","license":"GPL-3.0"},"color":"#D8BFD8","icon":"font-awesome/fa-cogs"},{"id":"2c77f2ec0596b24f","type":"function","z":"0dcc892460200b20","name":"Send to Jupyter","func":"let session = global.get(\"jupyter_session\");\n\nif (session != undefined) {\n    const future = session.kernel.requestExecute({ code: msg.payload, silent: false });\n    future.onIOPub = function(reply) {\n        if (reply.header.msg_type == 'stream') {\n            msg.payload = reply.content.text;\n            while (msg.payload.slice(-1) == \"\\n\" || msg.payload.slice(-1) == \"\\r\") {\n                msg.payload = msg.payload.slice(0, -1);\n            }\n\n            if (reply.content.text.length > 1000) {\n                console.log(\n                    reply.content.text.substring(0, 499) +\n                    \"\\n...\\n\" +\n                    reply.content.text.substring(reply.content.text.length - 500)\n                );\n            } else {\n                console.log(reply.content.text);\n            }\n            \n        }\n    };\n    future.onReply = function(reply) {\n        if (reply.content.status == 'ok') {\n            msg.code = 0;\n            // console.log('Python execution OK!');\n        }\n        else {\n            console.log(\"Something went wrong with the python execution:\");\n            console.log(reply.content.evalue);\n            msg.code = 1;\n        }\n    };\n    future.done.then(function(){\n        node.send(msg);\n    }).catch(function (err) {\n        console.error(err);\n    });\n\n    msg.payload = \"\";\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":200,"y":80,"wires":[[]]},{"id":"ecf996dbcbeedbfd","type":"subflow","name":"Get Servers","info":"Simply run the `jupyter server list` command and parse the output as an array of objects containing server properties.","category":"Jupyter","in":[{"x":60,"y":80,"wires":[{"id":"4ea97c5b74114d39"}]}],"out":[{"x":580,"y":80,"wires":[{"id":"5e6abde9f34b8fc0","port":0}]}],"env":[],"meta":{"module":"node-red-jupyter-tools","version":"0.1","author":"Paulo Felipe Jarschel <[email protected]>","desc":"Get a list of running Jupyter servers","keywords":"jupyter, python","license":"GPL-3.0"},"color":"#D8BFD8","icon":"font-awesome/fa-list-ul"},{"id":"4ea97c5b74114d39","type":"exec","z":"ecf996dbcbeedbfd","command":"jupyter server list","addpay":"","append":"","useSpawn":"true","timer":"","winHide":false,"oldrc":false,"name":"","x":230,"y":80,"wires":[["5e6abde9f34b8fc0"],["5e6abde9f34b8fc0"],[]]},{"id":"5e6abde9f34b8fc0","type":"function","z":"ecf996dbcbeedbfd","name":"Get server list","func":"// Prepare output payload\nlet servers_info = [];\n\n// Check if payload is actually a string, and if it is the expected output\nif (typeof msg.payload === \"string\" || msg.payload instanceof String) {\n    if (msg.payload.indexOf(\"Currently running servers:\") >= 0) {\n        // Create array of servers \n        let server_list = msg.payload.split(\"\\r\\n\").slice(1, -1);\n\n        // Parse each server string to get server info\n        server_list.forEach(function(item, index){\n            let url_stub = item.split(\":\")[1];\n            let server_info = {\n                \"baseUrl\": \"http:\" + url_stub,\n                \"wsUrl\": \"ws:\" + url_stub,\n                \"port\": item.split(\":\")[2].split(\"/\")[0],\n                \"token\": item.split(\"?token=\")[1].split(\" :: \")[0],\n                \"dir\": item.split(\" :: \")[1],\n            }\n            servers_info.push(server_info);\n        });\n    }\n}\n\n// Set servers info as message payload\nmsg.payload = servers_info;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":80,"wires":[[]]},{"id":"e8b1d6f8290a4e3b","type":"subflow","name":"Jupyter Manager","info":"Manage Jupyter servers available to the flow.\r\n\r\nIt allows one server/kernel/session to be running globally (within node-red).\r\nAt the moment, it does not allow a connection to an existing server, but it should be easy to implement.\r\n\r\nThe allowed inputs are:\r\n 1. \"start\": Simply start a server, kernel and session if not already started;\r\n 2. \"restart\": Restart kernel/session (useful if something is wrong, without the need of messing with the server);\r\n 3. \"stop\": Stop the running server. If for some reason a running server was \"lost\" to the flow (e.g.: after a crash), it **WILL NOT BE STOPPED**;\r\n 4. \"stop all\": Stop all running servers on the machine. **USE CAUTION WITH THIS ONE!**\r\n\r\nThere is a status indicator that runs at a 1 second interval. If the flow is connected to a server, it will be indicated.\r\n\r\nIt doesn't return anything useful.","category":"Jupyter","in":[{"x":60,"y":520,"wires":[{"id":"a93dad4d58c7544b"}]}],"out":[{"x":1280,"y":480,"wires":[{"id":"49419187d17c94fa","port":0},{"id":"f4fa13f4f5984f4a","port":0}]}],"env":[],"meta":{"module":"node-red-jupyter-tools","version":"0.1","author":"Paulo Felipe Jarschel <[email protected]>","desc":"Manage Jupyter servers available to the flow","keywords":"jupyter, python"},"color":"#D8BFD8","icon":"font-awesome/fa-server","status":{"x":300,"y":120,"wires":[{"id":"dac568d94b1d9143","port":0}]}},{"id":"a93dad4d58c7544b","type":"switch","z":"e8b1d6f8290a4e3b","name":"Start/Stop","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"start","vt":"str"},{"t":"eq","v":"reset","vt":"str"},{"t":"eq","v":"stop","vt":"str"},{"t":"eq","v":"stop all","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":220,"y":520,"wires":[["33577d1edcdb882a"],["75e5f62dfe062d31"],["e4676eb8a4fb9f64"],["ed5dad1289c76560"]]},{"id":"6788e19190e77d1b","type":"comment","z":"e8b1d6f8290a4e3b","name":"Commands are: start, reset, stop, stop all","info":"","x":240,"y":460,"wires":[]},{"id":"ed5dad1289c76560","type":"subflow:ecf996dbcbeedbfd","z":"e8b1d6f8290a4e3b","name":"","x":590,"y":700,"wires":[["5df34bd1a9b1df64"]]},{"id":"5df34bd1a9b1df64","type":"function","z":"e8b1d6f8290a4e3b","name":"Stop all","func":"// Check if message payload is actually an array\nif(Array.isArray(msg.payload)) {\n    let servers = RED.util.cloneMessage(msg).payload;\n\n    // Stop session\n    if (global.get(\"jupyter_session\") != undefined) {\n        global.get(\"jupyter_session\").shutdown();\n    }\n\n    // Stop all found servers\n    servers.forEach(function(item) {\n        if (typeof item.port === 'string' || item.port instanceof String) {\n            msg.payload = item.port;\n            node.send(msg);\n        }\n    });\n    \n    // Clear globals\n    global.set(\"jupyter_port\", undefined);\n    global.set(\"jupyter_token\", undefined);\n    global.set(\"jupyter_wsUrl\", undefined);\n    global.set(\"jupyter_baseUrl\", undefined);\n    global.set(\"jupyter_session\", undefined);\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":700,"wires":[["a78a0fb4dc99bd11"]]},{"id":"a78a0fb4dc99bd11","type":"exec","z":"e8b1d6f8290a4e3b","command":"jupyter server stop","addpay":"payload","append":"","useSpawn":"true","timer":"0","winHide":true,"oldrc":false,"name":"","x":970,"y":660,"wires":[[],[],["49419187d17c94fa"]]},{"id":"057e23ae535722d6","type":"function","z":"e8b1d6f8290a4e3b","name":"Find invoked server","func":"// Check if message payload is actually an array\nif(Array.isArray(msg.payload)) {\n    let servers = RED.util.cloneMessage(msg).payload;\n\n    // Check if any of the running servers was invoked from here\n    servers.forEach(function(item) {\n        if (typeof item.port === 'string' || item.port instanceof String) {\n            if (item.port == global.get(\"jupyter_port\")) {\n                // If it was, stop session, the server, and clear globals\n                if (global.get(\"jupyter_session\") != undefined) {\n                    global.get(\"jupyter_session\").shutdown();\n                }\n                global.set(\"jupyter_port\", undefined);\n                global.set(\"jupyter_token\", undefined);\n                global.set(\"jupyter_wsUrl\", undefined);\n                global.set(\"jupyter_baseUrl\", undefined);\n                global.set(\"jupyter_session\", undefined);\n                \n                msg.payload = item.port;\n                node.send(msg);\n            }\n        }\n    });\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":720,"y":620,"wires":[["a78a0fb4dc99bd11"]]},{"id":"e4676eb8a4fb9f64","type":"subflow:ecf996dbcbeedbfd","z":"e8b1d6f8290a4e3b","name":"","x":530,"y":620,"wires":[["057e23ae535722d6"]]},{"id":"e36ce5fd3b39e4e4","type":"function","z":"e8b1d6f8290a4e3b","name":"Check existing servers","func":"let is_running = false;\n\n// Check if there is information of a running server\nif (global.get(\"jupyter_port\") != undefined) {\n    // If so, check if it is actually running\n    \n    // Check if message payload is actually an array\n    if(Array.isArray(msg.payload)) {\n        // Check if global port corresponds to a running server\n        msg.payload.forEach(function(item) {\n            if (typeof item.port === 'string' || item.port instanceof String) {\n                if (item.port == global.get(\"jupyter_port\")) {\n                    // If so, re-set globals and check session status\n                    is_running = true;\n                    global.set(\"jupyter_port\", item.port);\n                    global.set(\"jupyter_token\", item.token);\n                    global.set(\"jupyter_wsUrl\", item.wsUrl);\n                    global.set(\"jupyter_baseUrl\", item.baseUrl);\n                    \n                    if (global.get(\"jupyter_session\") == undefined) {\n                        msg.payload = \"start session\";\n                    } else {\n                        if (global.get(\"jupyter_session\").serverSettings.token != item.token) {\n                            global.get(\"jupyter_session\").shutdown();\n                            global.set(\"jupyter_session\", undefined);\n                            msg.payload = \"start session\";\n                        } else {\n                            msg.payload = \"\";\n                        }\n                    }\n                }\n            }\n        });\n    }\n}\n\n// If not, just go on starting a new one\nif (!is_running) {\n    // Clear all globals and session info\n    if (global.get(\"jupyter_session\") != undefined) {\n        global.get(\"jupyter_session\").shutdown();\n    }\n    global.set(\"jupyter_port\", undefined);\n    global.set(\"jupyter_token\", undefined);\n    global.set(\"jupyter_wsUrl\", undefined);\n    global.set(\"jupyter_baseUrl\", undefined);\n    global.set(\"jupyter_session\", undefined);\n\n    msg.payload = \"start server\";\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":640,"y":140,"wires":[["08afc66a86721576"]]},{"id":"33577d1edcdb882a","type":"subflow:ecf996dbcbeedbfd","z":"e8b1d6f8290a4e3b","name":"","x":430,"y":140,"wires":[["e36ce5fd3b39e4e4"]]},{"id":"08afc66a86721576","type":"switch","z":"e8b1d6f8290a4e3b","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"start server","vt":"str"},{"t":"eq","v":"start session","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":830,"y":140,"wires":[["28a8dd406840cde4"],["c13c555871daa215"]]},{"id":"28a8dd406840cde4","type":"exec","z":"e8b1d6f8290a4e3b","command":"jupyter server","addpay":"","append":"","useSpawn":"true","timer":"","winHide":false,"oldrc":false,"name":"","x":700,"y":260,"wires":[["d88a58f4ed2188a3"],["d88a58f4ed2188a3"],["d88a58f4ed2188a3"]]},{"id":"d88a58f4ed2188a3","type":"function","z":"e8b1d6f8290a4e3b","name":"Parse URL and token","func":"let main_identifier = \"copy and paste one of these URLs:\\r\\n\"\nif (typeof msg.payload === 'string' || msg.payload instanceof String) {\n    if (msg.payload.indexOf(main_identifier) >= 0) {\n        let full_url = msg.payload.split(main_identifier)[1].split(\"\\r\\n\")[0];\n        let url_stub = full_url.split(\":\")[1];\n        global.set(\"jupyter_port\", full_url.split(\":\")[2].split(\"/\")[0]);\n        global.set(\"jupyter_token\", full_url.split(\"?token=\")[1].split(\" :: \")[0]);\n        global.set(\"jupyter_wsUrl\", \"ws:\" + url_stub);\n        global.set(\"jupyter_baseUrl\", \"http:\" + url_stub);\n\n        msg.payload = \"start session\";\n        return msg;\n    }\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":960,"y":260,"wires":[["c13c555871daa215"]]},{"id":"c13c555871daa215","type":"function","z":"e8b1d6f8290a4e3b","name":"Session Manager","func":"// Import Jupyter Services\n// const services = require('@jupyterlab/services');\nconst services = global.get('jp_services');\n\nif (msg.payload == \"start session\") {\n    // Check if session exists, and end it\n    let resetting = false;\n    if (global.get(\"jupyter_session\") != undefined) {\n        resetting = true;\n        console.log('Ending session…');\n        global.get(\"jupyter_session\").shutdown().then(function () {\n            global.set(\"jupyter_session\", undefined);\n            new_session();\n        });\n    }\n\n    // Start a new session.\n    function new_session() {\n        const options = {\n            path: 'foo.ipynb',\n            type: 'notebook',\n            name: 'foo.ipynb',\n            kernel: {\n                name: 'python'\n            }\n        };\n\n        let port = global.get(\"jupyter_port\");\n        let token = global.get(\"jupyter_token\");\n        let baseUrl = global.get(\"jupyter_baseUrl\") + \":\" + port + \"/\";\n        let wsUrl = global.get(\"jupyter_wsUrl\") + \":\" + port + \"/\";\n        console.log('Starting session…');\n\n        const kernelManager = new services.KernelManager();\n        kernelManager.serverSettings.baseUrl = baseUrl;\n        kernelManager.serverSettings.wsUrl = wsUrl;\n        kernelManager.serverSettings.token = token;\n\n        const sessionManager = new services.SessionManager({ kernelManager });\n        sessionManager.serverSettings.baseUrl = baseUrl;\n        sessionManager.serverSettings.wsUrl = wsUrl;\n        sessionManager.serverSettings.token = token;\n\n        sessionManager.startNew(options).then(function (s) {\n            global.set(\"jupyter_session\", s);\n            node.send(msg);\n        });\n    }\n\n    if (!resetting) {\n        new_session();\n    }\n}\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":400,"wires":[["82f96099b7551fdf"]]},{"id":"82f96099b7551fdf","type":"template","z":"e8b1d6f8290a4e3b","name":"Base imports","field":"payload","fieldType":"msg","format":"python","syntax":"plain","template":"import os\nimport sys\nimport json\nprint(\"Base imports ok\")","output":"str","x":910,"y":400,"wires":[["f4fa13f4f5984f4a"]]},{"id":"54771eacd975f1bb","type":"comment","z":"e8b1d6f8290a4e3b","name":"Manage stopping servers","info":"","x":690,"y":560,"wires":[]},{"id":"97a99757b0a0dc12","type":"comment","z":"e8b1d6f8290a4e3b","name":"Manage starting servers and resetting kernels","info":"","x":710,"y":80,"wires":[]},{"id":"49419187d17c94fa","type":"function","z":"e8b1d6f8290a4e3b","name":"Force code 0","func":"msg.payload.code = 0;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1100,"y":520,"wires":[["e508d1d9930d09ac"]]},{"id":"b57feb0361bc771b","type":"inject","z":"e8b1d6f8290a4e3b","name":"Check status","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":120,"y":80,"wires":[["268a1ab4144dc4d0"]]},{"id":"268a1ab4144dc4d0","type":"subflow:ecf996dbcbeedbfd","z":"e8b1d6f8290a4e3b","name":"","x":110,"y":120,"wires":[["dac568d94b1d9143"]]},{"id":"dac568d94b1d9143","type":"function","z":"e8b1d6f8290a4e3b","name":"Check existing servers","func":"let is_running = false;\n\n// Check if there is information of a running server\nif (global.get(\"jupyter_port\") != undefined) {\n    // If so, check if it is actually running\n    // Check if message payload is actually an array\n    if(Array.isArray(msg.payload)) {\n        // Check if global port corresponds to a running server\n        msg.payload.forEach(function(item) {\n            if (typeof item.port === 'string' || item.port instanceof String) {\n                if (item.port == global.get(\"jupyter_port\")) {\n                    is_running = true;\n                }\n            }\n        });\n    }\n}\n\n// Set status\nif (is_running) {\n    msg.payload = \"Active on port \" + global.get(\"jupyter_port\");\n} else {\n    msg.payload = \"Stopped\";\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":160,"y":180,"wires":[[]]},{"id":"f4fa13f4f5984f4a","type":"subflow:0dcc892460200b20","z":"e8b1d6f8290a4e3b","name":"","x":1120,"y":400,"wires":[["e508d1d9930d09ac"]]},{"id":"75e5f62dfe062d31","type":"subflow:ecf996dbcbeedbfd","z":"e8b1d6f8290a4e3b","name":"","x":390,"y":500,"wires":[["5fed117cbe362397"]]},{"id":"5fed117cbe362397","type":"function","z":"e8b1d6f8290a4e3b","name":"Check existing","func":"// Check if there is information of a running server\nif (global.get(\"jupyter_port\") != undefined) {\n    // If so, check if it is actually running\n    // Check if message payload is actually an array\n    if(Array.isArray(msg.payload)) {\n        // Check if global port corresponds to a running server\n        msg.payload.forEach(function(item) {\n            if (typeof item.port === 'string' || item.port instanceof String) {\n                if (item.port == global.get(\"jupyter_port\")) {\n                    // If so, re-set globals and check session status\n                    global.set(\"jupyter_port\", item.port);\n                    global.set(\"jupyter_token\", item.token);\n                    global.set(\"jupyter_wsUrl\", item.wsUrl);\n                    global.set(\"jupyter_baseUrl\", item.baseUrl);\n                    \n                    msg.payload = \"start session\";\n                }\n            }\n        });\n    }\n} else {\n    msg.payload = \"\"\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":440,"wires":[["c13c555871daa215"]]},{"id":"e508d1d9930d09ac","type":"junction","z":"e8b1d6f8290a4e3b","x":1300,"y":40,"wires":[["09a29d5ea529f1fd"]]},{"id":"09a29d5ea529f1fd","type":"junction","z":"e8b1d6f8290a4e3b","x":20,"y":20,"wires":[["268a1ab4144dc4d0"]]},{"id":"c8d536e94f59d49d","type":"tab","label":"Run Python code","disabled":false,"info":"","env":[]},{"id":"dcf49513c37c4ddb","type":"inject","z":"c8d536e94f59d49d","name":"Run","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":250,"y":400,"wires":[["0ee359c162a15c3c"]]},{"id":"0ee359c162a15c3c","type":"template","z":"c8d536e94f59d49d","name":"Python Code","field":"payload","fieldType":"msg","format":"python","syntax":"plain","template":"print('running')\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nprint('import ok')\n\nn = 1000\nx = np.linspace(-10, 10, n)\ny = np.sin(x) + 0.4*(np.random.random(n) - 0.5)\nprint('vars ok')\n\nplt.plot(x, y)\nplt.savefig('foo.png', dpi=300)\nprint('plot done')\n\ntestvar = {\"x\": 45, \"lol\": \"test\", \"bbb\": (1,5,'oi'), \"vfr\": {\"g\": 5, \"l\": \"puco\", \"vik\": np.random.randint(0, 10, 10)}, \"kkkk\": [1,2,3,4,5], \"arai\": np.random.randint(0, 10, (3,3))}","output":"str","x":410,"y":400,"wires":[["9fe2d0c268f5819b"]]},{"id":"2d2be768ad57a7c7","type":"inject","z":"c8d536e94f59d49d","name":"Get var","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"['x', 'y', 'testvar']","payloadType":"jsonata","x":250,"y":520,"wires":[["5ef27751bd10911a"]]},{"id":"6f3517806310b255","type":"comment","z":"c8d536e94f59d49d","name":"Create data","info":"","x":250,"y":360,"wires":[]},{"id":"6b3aba92636caf35","type":"comment","z":"c8d536e94f59d49d","name":"Get data","info":"","x":240,"y":480,"wires":[]},{"id":"b2928b84d4584cf8","type":"debug","z":"c8d536e94f59d49d","name":"Var","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":630,"y":520,"wires":[]},{"id":"5217f37a5ad5ece8","type":"subflow:e8b1d6f8290a4e3b","z":"c8d536e94f59d49d","name":"","x":360,"y":140,"wires":[[]]},{"id":"4bd7fbb0073d0e52","type":"inject","z":"c8d536e94f59d49d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"stop all","payloadType":"str","x":110,"y":200,"wires":[["5217f37a5ad5ece8"]]},{"id":"77c61e0c36eec02c","type":"inject","z":"c8d536e94f59d49d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"stop","payloadType":"str","x":110,"y":160,"wires":[["5217f37a5ad5ece8"]]},{"id":"5320355b6831a8e5","type":"inject","z":"c8d536e94f59d49d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"reset","payloadType":"str","x":110,"y":120,"wires":[["5217f37a5ad5ece8"]]},{"id":"0339587784c035f8","type":"inject","z":"c8d536e94f59d49d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"start","payloadType":"str","x":110,"y":80,"wires":[["5217f37a5ad5ece8"]]},{"id":"33c4729ee4bb86cf","type":"comment","z":"c8d536e94f59d49d","name":"Manage Jupyter servers and kernels","info":"","x":360,"y":100,"wires":[]},{"id":"9fe2d0c268f5819b","type":"subflow:0dcc892460200b20","z":"c8d536e94f59d49d","name":"Execute Python Code","x":620,"y":400,"wires":[["0d610d0ac2d6f3c6"]],"icon":"font-awesome/fa-file-text-o"},{"id":"0d610d0ac2d6f3c6","type":"debug","z":"c8d536e94f59d49d","name":"Code output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":830,"y":400,"wires":[]},{"id":"5ef27751bd10911a","type":"subflow:30cc2af1b6f3f4e2","z":"c8d536e94f59d49d","name":"Get Data from Python","x":440,"y":520,"wires":[["b2928b84d4584cf8"]]}]

Flow Info

Created 11 months ago
Updated 10 months, 3 weeks ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • comment (x6)
  • debug (x2)
  • exec (x3)
  • function (x12)
  • inject (x7)
  • join (x1)
  • split (x1)
  • switch (x3)
  • template (x4)
Other
  • junction (x2)
  • subflow (x4)
  • subflow:0dcc892460200b20 (x4)
  • subflow:30cc2af1b6f3f4e2 (x1)
  • subflow:e8b1d6f8290a4e3b (x1)
  • subflow:ecf996dbcbeedbfd (x5)
  • tab (x1)

Tags

  • jupyter
  • python
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option