Skip to content

Dynamic Data provision Dash Bord

Johannes edited this page Jan 28, 2020 · 8 revisions

Getting started with dynamic data provision

A dashboard must show data which can be updated at any time. In some situation we will need real-time data presentation or at least what is possible within the tcp/ip protocol processing time and the browser display time. We start with a slightly less strict requirement. The easiest way to provide dynamic data for the chart is the use of ajax calls for Bokeh charts. Luckily this is already implemented as a standard way in Bokeh charts. Ajax data structures behave the same as usual data structures and may be used as a seamless way to update data in a regular interval. Here we show how to update a number in a label. For that end we have to use LabelSet because that is the only dynamically updated data structure in combination with a text shown on the chart. We put the following code in a separate class ChartProvider in our Python back-end, which has then a function like:

class ChartProvider():

  def chartExample(self, id_):
    # Bokeh related code
    adapter = CustomJS(code="""
    const value = cb_data.response.number;
    const result = {label: [value]};
    return result;
    """)

    # create a new plot
    q = np.array([0,  0, 0, -1, -1,  1, 1]) - 2
    r = np.array([0, -1, 1,  0,  1, -1, 0]) 

    source = AjaxDataSource(data_url='http://localhost:9000/number',
                            polling_interval=300, adapter=adapter)

    p = figure(plot_height=HEIGHT, plot_width=WIDTH, background_fill_color="lightgrey",
               title="Number repesentation", toolbar_location=None, 
               match_aspect=True, x_range=[-6,2])
    p.grid.visible = False
    p.axis.visible = False
    p.hex_tile(q, r, size=1, fill_color=["firebrick"]*3 + ["navy"]*4,
               line_color="white", alpha=0.5)

    labels = LabelSet(x=-0.75, y=-0.75, text='label', level='glyph',
                      source=source, text_font_size="40pt")

    p.add_layout(labels)
    chart_item = json_item(p)
    return chart_item

This code snippet shows how the source may be updated every 300 ms and how the data is processed further within the adapter which is a small snippet of JavaScript Code. On Python side the AjaxDataSource behaves mostly like every static data source which is of the format

data = {'x_values': [1, 2, 3, 4, 5],
        'y_values': [6, 7, 2, 3, 6]}
ColumnDataSource(data=data)

To provide continuous data the ajax way we must use an URL that is supported by our web server. As stated previously we use aiohttp and as such we may include this route as a POST route as follows:

from aiohttp import web

app = web.Application(debug=False)
app.add_routes([web.get('/admin', admin),
                web.post('/number', number)])

async def number(request):
    n = np.random.random()
    return web.json_response({"number": "%.2f" % n})

In the end, we can include the chart_item which comes out of a ChartProvider the same way we described in detail previously in our article. The chart_item is a json formatted chart which is self-contained. That means also that the chart has the data fetch mechanism ajax already included. In the following picture we see the chart figure included in a typical angular material card:

video (youtube) chart figure example

Bokeh provides an easy way of ajax updates. But sadly Bokeh has not yet a simple function, which only redraws the chart on JavaScript side. As a reminder, we use the call of the global included Bokeh module, which we import in our angular component via:

declare var Bokeh: any;

Bokeh.embed.embed_item(item, id);

The main task on the angular side is to provide the data for the embed_item function call. Since we use a websocket connection, we send the chart_item from python to angular. It may also be provided by a REST-api. Our websocket connection by the way is more viable if we need data just in time from the server. The ajax call on the other hand is done by polling, which has several drawbacks. One of them is, the Webserver has work every time the client asks for data even if the data has not changed. So long story short. We can provide data like first chart data or other event driven data via websocket but update data for the chart at the moment is limited to ajax calls. We would love to circumvent the ajax call in the future and switch to event driven calls from back-end side. If you think about the solution, why not every time send the full chart via websocket and call the embed_item method, it is not a solution. The way Bokeh embeds the chart is very costly, thus we would never reach the update rate we would like to have.

Summary

This example shows how to implement a periodically updated Bokeh chart within an Angular app, which is one part of our final goal that is to create a dynamic dashboard. For the data update we have used the ajax method because it is an already built in method in Bokeh and we do not have to tweak our websocket connection. On the python side the data provision is a more or less simple task but it is important that we make sure to use non blocking calls, that means a consistent use of asynchronous methods.