<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Welcome to the Demo - v0.1.4
This notebook provides a simulated workflow for using IPySigma with networkx.
We only need to import one class, IPySig, the main controller object along with networkx.
Step1:  To start, we only need to import the IPySig object from the ipysig.core package. This is a singleton controller class that manages our sigma.js visualization connections. The root directory of the node.js app is passed in on instantiation. 
If the demo notebook is being run inside the repo, this directory is simply './app', but in production this would change to wherever the user installed the node app on their system. 
If all goes well, IPySig will initialize the express server and print the PID number for the running process. 
On instantiation, the API also injects a set of custom methods onto the nx.Graph class which allows the controller to interface with the graph object and reshape the networkx object into a format suitable both for web socket transfer across the node server and readable by Sigma.js on the frontend.
Step2:  Let's make a network graph from the stars dataset. This was a custom search from the NASA ADS api and contains relationships between authors and journals for the keyword search "stars".
To bring up the visualization tabs we pass in the object instance and a unique identifier to the controllers connect method. This key name is important because it binds the browser tab's socket id to a reference of the object.
After executing each cell the visualization tab should open and by entering a title name for the graph pressing Load Graph, the graph visualization object should appear. This object reference is stored both in the IPySig controller instance and in the browser via a Loki.js datastore.
In our current demo, v0.1.0, we can only store one graph at a time per session.
Loading the Stars Dataset
Step3:  The stars_graph was saved earlier and loaded via pickle for convenience, but nx graphs can obviously be created on the fly, loaded from a database or csv or come into the notebook from any other data source.
In order to get the most out of IPySigma, certain node and edge list attributes need to be added to the graph object. The sigma graph loader looks for a few key headers that the user needs to provide. The keyword "label" provides the visible label for each node and "node_type" is used to cataegorize each node and set color attributes.
Note that the system will still work without these labels, however, the user will lose some of the nicest features of Sigma.js.
Step4:  Connecting the graph
We connect the graph to a new browser session by calling the "connect" method on the controller and passing in the object and a key-name for the graph object. 
This method call spins up a browser tab and binds the web socket to this graph instance.
From this point, we can provide a graph name in the browser tab and hit the load_graph button. This sends a signal to IPySig to call the export_graph_instance() method and jsonify the result.
This method call essentially packages our networkx object and adds attributes to the graph instance that is needed by Sigma.js. It also adds some centrality measures for free, along with any other user given attributes.
The user can now explore the nodes of the graph freely in the browser tab and get node information by toggling a simple jquery based inspector.
Step5:  Quite a bit of functionality for our IPySig controller and node application is still under development.
Our current demo does not yet support disconnect/reconnect methods and auto-kill of the server process when the kernel is accidentally or unexpectedly shuts down. Also some care needs to be taken in the current version so that the controller has a chance to boot the express server before a browser tab and web socket connection is open. 
Future implementation will also include the ability to store multiple graphs in a single browser session tab, by storing names in an in memory datastore.
To cleanly exit the application for now we need to kill the express process manually.  
Just for fun a random graph...
Connecting a new graph will spin it up in a new tab. Without adding labels and node_types to the graph, default values are used.
<ASSISTANT_TASK:>
Python Code:
# notebook to simulate a jupyter working environment
# the python module gets imported here...
from ipysig.core import IPySig
import networkx as nx
Explanation: Welcome to the Demo - v0.1.4
This notebook provides a simulated workflow for using IPySigma with networkx.
We only need to import one class, IPySig, the main controller object along with networkx.
End of explanation
ctrl = IPySig('./app') # note this should point to the root folder of the express app
Explanation: To start, we only need to import the IPySig object from the ipysig.core package. This is a singleton controller class that manages our sigma.js visualization connections. The root directory of the node.js app is passed in on instantiation. 
If the demo notebook is being run inside the repo, this directory is simply './app', but in production this would change to wherever the user installed the node app on their system. 
If all goes well, IPySig will initialize the express server and print the PID number for the running process. 
On instantiation, the API also injects a set of custom methods onto the nx.Graph class which allows the controller to interface with the graph object and reshape the networkx object into a format suitable both for web socket transfer across the node server and readable by Sigma.js on the frontend.
End of explanation
import pickle
import os
pkl_g = os.path.abspath(os.path.join('.','IPySig_demo_data', 'stars_test_graph.pkl'))
stars_graph = pickle.load(open(pkl_g, "rb" )) 
Explanation: Let's make a network graph from the stars dataset. This was a custom search from the NASA ADS api and contains relationships between authors and journals for the keyword search "stars".
To bring up the visualization tabs we pass in the object instance and a unique identifier to the controllers connect method. This key name is important because it binds the browser tab's socket id to a reference of the object.
After executing each cell the visualization tab should open and by entering a title name for the graph pressing Load Graph, the graph visualization object should appear. This object reference is stored both in the IPySig controller instance and in the browser via a Loki.js datastore.
In our current demo, v0.1.0, we can only store one graph at a time per session.
Loading the Stars Dataset
End of explanation
stars_graph.nodes(data=True)[:5]
stars_graph.edges(data=True)[:10]
Explanation: The stars_graph was saved earlier and loaded via pickle for convenience, but nx graphs can obviously be created on the fly, loaded from a database or csv or come into the notebook from any other data source.
In order to get the most out of IPySigma, certain node and edge list attributes need to be added to the graph object. The sigma graph loader looks for a few key headers that the user needs to provide. The keyword "label" provides the visible label for each node and "node_type" is used to cataegorize each node and set color attributes.
Note that the system will still work without these labels, however, the user will lose some of the nicest features of Sigma.js.
End of explanation
ctrl.connect(stars_graph,'stars')
Explanation: Connecting the graph
We connect the graph to a new browser session by calling the "connect" method on the controller and passing in the object and a key-name for the graph object. 
This method call spins up a browser tab and binds the web socket to this graph instance.
From this point, we can provide a graph name in the browser tab and hit the load_graph button. This sends a signal to IPySig to call the export_graph_instance() method and jsonify the result.
This method call essentially packages our networkx object and adds attributes to the graph instance that is needed by Sigma.js. It also adds some centrality measures for free, along with any other user given attributes.
The user can now explore the nodes of the graph freely in the browser tab and get node information by toggling a simple jquery based inspector.
End of explanation
G = nx.random_graphs.newman_watts_strogatz_graph(80,15,0.2)
ctrl.connect(G, 'newman_watts')
ctrl.kill_express_process()
Explanation: Quite a bit of functionality for our IPySig controller and node application is still under development.
Our current demo does not yet support disconnect/reconnect methods and auto-kill of the server process when the kernel is accidentally or unexpectedly shuts down. Also some care needs to be taken in the current version so that the controller has a chance to boot the express server before a browser tab and web socket connection is open. 
Future implementation will also include the ability to store multiple graphs in a single browser session tab, by storing names in an in memory datastore.
To cleanly exit the application for now we need to kill the express process manually.  
Just for fun a random graph...
Connecting a new graph will spin it up in a new tab. Without adding labels and node_types to the graph, default values are used.
End of explanation
<END_TASK>