Bokeh + Flask + Realtime: Issue with components() return values

Hello - I’m trying to figure out why a plot I’m passing to the components() method is failing to return the key:value pairs { “modelid” : something } and { “elementid” : something } within the <script> variable “render_items”.

Right now, “render items” looks like this in the <script> printout:

    var render_items = [{"docid":"73c7618a-ac07-4c1b-8c7d-6fd7c45e09c3","root_ids":["1004"],"roots":{"1004":"6d7217d4-2782-45c9-aed2-add321249b95"}}];

For some context, I want to embed a series of Bokeh figures into a Flask-powered html, and I’ve separated bulk of the Bokeh graph building logic from the routes.py script used to render the html template. I’ve done this to keep the routes.py script tidy, since it has a lot of other stuff going on. Right now, I’m in the early stage of development and just want to get a plot to render to the html page.

This is what the user defined class (which is named BokehObject) method being called in routes.py looks like:

def dotplot_skeleton(self):
    source = ColumnDataSource()
    source.data =  dict(
        x = self.report_df["NAME"].to_list(),
        y = self.report_df["READ_COUNT"].to_list() )
    dotplot = figure(plot_height=600, plot_width=720)
    dotplot.circle(x="x", y="y", source=source, size=5, line_color=None)
    dotplot.xaxis.axis_label = "NAME"
    dotplot.yaxis.axis_label = "READ_COUNT"
    return dotplot

For reference, report.df is a dataframe populated by a different class method. It is working correctly.
Then, in routes.py, I retrieve the plot defined by the class, and pass it to the components() method:

bo = BokehObject(Results.query.get(results_id))
    dotplot = bo.dotplot
    script, div = components(dotplot)
    return render_template('items/vis.html',
                            plot_script=script, plot_div=div, 
                            js_resources=INLINE.render_js(), 
                           css_resources=INLINE.render_css())

Finally, my .html document ‘vis.html’, which lives in a templates folder called ‘items’, looks like so:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flask + Bokeh</title>
    {{ js_resources|indent(4)|safe }}
    {{ css_resources|indent(4)|safe }}
    {{ plot_script|indent(4)|safe }}
</head>
<body>
    <h1 style="text-align: center; margin-bottom: 40px;">Flask + Bokeh</h1>
    <div style="display: flex; justify-content: center;">
    {{ plot_div|indent(4)|safe }}
    </div>
</body>
</html>

Earlier, as the Bokeh Doc for bokeh.embed suggests, the html document also contained the lines:

<script src="https://cdn.bokeh.org/bokeh/release/bokeh-2.3.1.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.3.1.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.3.1.min.js"></script>

But these did not seem to have an effect on the outcome.

I’ve been attempting to follow an example of this implementation I found here:

Interactive Realtime Visualiation with Bokeh+Flask

The main difference between how the example does it and I’m trying to do it is how the source.data attribute is being populated. In their case, they pull in a CSV from a website called “easybase.io”. I can’t do that due to the sensitivity of my data, so instead I’m just flattening the returned dataframe report_df with a .to_list() call.

This blog describes js_resources() and css_resources() as “general Bokeh-required JavaScript” and “General Bokeh-required CSS”, respectively; but it did a poor job explaining how the INLINE call to these methods works. I suspect that this is the root cause of my problem, given that all my variables seem to be populating correctly.

I’ve checked and double checked my dependancies. I don’t think those are causing the issue. I’ve been referencing documentation for v.2.3.1 and have that version installed.

I’ve done pretty extensive testing to ensure that the dataframe being passed to source (which you see when I’m constructing the plot in my BokehObject) is filling out properly. I’ve also found that within the source attribute source.data, the “x” and “y” I define there seem to be correct. When I run my flask application, I get the following:

So, the plot appears to be constructing, but the data is not being passed, or isn’t plotting, or something.

Why do I not see modelid or elementid in the <script> dict? I’m guessing they are crucial to getting the data to plot.

If you want to try and reproduce my error, you will need two files at minimum; an html that retrieves stuff from the render_template() method in the routes file, and of course that routes.py file that defines the plot and generates a <script> and <div>.

Hi @loremaster.cerberus please edit your post to use code formatting so that the code is intelligible (either with the </> icon on the editing toolbar, or triple backtick ``` fences around the code blocks)

Thanks Bryan - it’s my first time posting on the forum. I’ve made those edits.

The fact that any Bokeh content is rendering at all means there is no issue with the resources or the embedding. All of that is working. The most likely explanation is that you are not embedding the thing you intend to embed. Some ideas that come to mind:

  • bo.dotplot is not what you think it is, specifically not the plot created by the dotplot_skeleton method, or
  • the data you are putting in the data source are empty lists

It’s not really possible to speculate beyond that without a Minimal Reproducible Example to actually run and investigate directly. At this stage I would simply start doing some “printf debugging” to examine the program state and isolate where it differs from my expectations.

I’ll work on getting a minimal example up shortly.
For now though, I can say that I don’t think either of your suggestions are my problem!

When I print bo.dotplot and source (both of which are BokehObject class attributes I created with user-defined methods), I get:

Figure(id='1004', ...)
ColumnDataSource(id='1040', ...)

Which makes sense when I compare that alongside what I get for the <script> printout - it’s very long, so here is a snippet:

var docs_json = ' { ... stuff goes here {{"attributes":{},"id":"1047","type":"BasicTickFormatter"},{"attributes":{"source":{"id":"1003"}},"id":"1039","type":"CDSView"},{"attributes":{},"id":"1005","type":"DataRange1d"}],"root_ids":["1004"]},"title":"Bokeh Application","version":"2.3.1"}}';

The source and attributes IDs are different, but the root_ids ID matches!

I’m also getting data from both the “x” and “y” of bo.dotplot. Here are some other snippets:

{'x': ['Viruses', 'Bacteria', ...], 'y': [126486, 211888, ...]}

So that bo.dotplot definitely is being populated with data. Minimal example coming up soon.

If your x-values are categorical (i.e. are strings), then you need to to configure an appropriate range with the categorical factors. There is a chapter on handling categorical data in the documentation:

https://docs.bokeh.org/en/latest/docs/user_guide/categorical.html

Bryan, I’ve (finally) created a minimum reproducible example in a separate forum post describing a similar problem. Find it here