Usage

Note

This is a plugin for the SPADE agent platform. Please visit the SPADE’s documentation to know more about this platform.

Plotting figures in html usually involves too much javascript code. To solve this there is a great python library called Bokeh that helps us to plot our own figures with python code and dinamycally thanks to a server embedded with bokeh.

This plugin provides a mixin to include a bokeh server in an agent and to register plots to be then rendered in your jinja2 templates of your agent’s web interface.

To use SPADE Bokeh Server in a project:

import getpass

import spade
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature

import spade_bokeh


class MyBokehAgent(spade_bokeh.BokehServerMixin, spade.agent.Agent):

    async def controller(self, request):
        script = self.bokeh_server.get_plot_script("/my_plot")

        return {"script": script}

    async def setup(self):

        self.web.add_get("/plot", self.controller, "plot.html")

        self.web.start(port=10000)
        self.bokeh_server.start()

        self.bokeh_server.add_plot("/my_plot", self.modify_doc)

    def modify_doc(self, doc):
        df = sea_surface_temperature.copy()
        source = ColumnDataSource(data=df)

        plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',
                      title="Sea Surface Temperature at 43.18, -70.43")
        plot.line('time', 'temperature', source=source)

        def callback(attr, old, new):
            if new == 0:
                data = df
            else:
                data = df.rolling('{0}D'.format(new)).mean()
            source.data = data

        slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
        slider.on_change('value', callback)

        doc.add_root(column(slider, plot))


async def main(jid, passwd):
    a = MyBokehAgent(jid, passwd)
    await a.start()

    await spade.wait_until_finished(a)

if __name__ == "__main__":

    jid = input("Agent JID> ")
    passwd = getpass.getpass()
    spade.run(main(jid, passwd))

In the example below there are 3 different blocks: the agent setup, the web controller and the bokeh plot creation.

Agent setup

To activate the spade_bokeh plugin in your agent you must follow some steps:
  • First of all your agent needs to inherit from spade_bokeh.BokehServerMixin.

    Warning

    All the mixins MUST be included before the spade.agent.Agent class, to respect the method resolution order (e.g. class MyAgent(MyMixin1, MyMixin2, ..., Agent)).

  • Next you need to start your bokeh_server with the following order: self.bokeh_server.start(). This method may accept two arguments: the hostname and the port of the bokeh_server. By default they are “localhost” and 5006.

    Warning

    Two agents can not share a same bokeh_server, so they must use different ports!

  • Finally, you can add new plots to your server using the add_plot function of bokeh_server. It accepts two arguments: the path for the plot in the bokeh server and the callback to build the plot.

    Note

    You can add as many plots as you need.

Bokeh plot creation

The method that you give to the add_plot call is the callback that will create the plot when the url path is queried. Inside this method you can create your plot following the Bokeh guidelines. As in the example, the method receives a doc argument, where the plots of your application will be rendered.

Hint

To learn more about how to create Bokeh plots please visit the Bokeh User Guide

Web controller

The final step to render your bokeh plots inside an agent view is to render the plot in a template managed by the SPADE’s web interface system.

Hint

Please, visit the SPADE’s documentation to know more about how to create a SPADE web interface for your agents.

The spade_bokeh plugin provides you a helper function to easily render your plots inside a jinja2 template. As in the example below, you can use the get_plot_script method with the path of the plot you want to render and it will return you the necessary javascript to render the plot (this javascript contains the URL and necessary code to connect to the bokeh server dinamycally).

Then you only need to render that script in your template as in the example:

<html lang="en">
    <head>
        <title>Bokeh Example</title>
    </head>

    <body>
        {{ script | safe}}
    </body>
</html>

Note

Note that you must safe escape the script with the safe jinja2 filter to avoid escaping the html tags.