I created a website that uses two bokeh plots (one map and one stacked bar plot) with the intention of deploying on a private network. When deploying the bokeh plots are not rendering. The only useful logging was a warning about depreciated syntax. Any intuition on why Bokeh visualizations would fail on a private network?
By default Bokeh loads its JavaScript runtime library BokehJS from a public CDN at cdn.bokeh.org
. If you are on an private/airgapped network then I expect examining your browser’s network tab in dev tools would show 404’s trying to load these. You will have to use “inline” resources. You can pass bokeh.resources.INLINE
to functions that accept a resources argument (you haven’t stated how you are outputting things so I can’t be any more specific), or you can set the environment variable BOKEH_RESOURCES=inline
before running whatever process creates the Bokeh output.
The only useful logging was a warning about depreciated syntax.
Note that Bokeh is really two libraries, one in Python and another one, that actually does almost all the real work, in JavaScript. Most of the time if there are problems in the browser, only the browser logs will be helpful to look at.
This is very helpful. It occurred to me that this part of the html template may also be causing the errors.
<body>
<h1>Latency Bar Plot</h1>
{{div1| safe}}
</body>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-api-2.3.3.min.js"></script>
Is there a way to render the plots without those references to the external website?
There is a large matrix of embed/output options. Really need more info about what you are specifically doing. Are you using components
in the notebook? How are those links getting embedded in the template currently?
I created a Django website where the plots are getting embedded in the template with variables assigned in the plot below. The home
function determines which variables get passed to the html template.
def home(request):
#df_gims = remove_col(df_gims, 'unnamed')
rm_table_one, rm_table_two = create_transposed_dfs(redmine_table)
df_map = create_df_projections(df_gims)
map_output = create_map(df_map) # Stacked bar chart with bokeh
if len(map_output) > 1:
layout = column(map_output[0], map_output[1])
else:
layout = column(map_output[0])
script, div = components(layout) # Map with Bokeh
p = create_latency_stacked_plot(df_latency) # Stacked bar chart with bokeh
layout = column(p) # Stacked bar chart with bokeh
script1, div1 = components(layout) # Stacked bar chart with bokeh
data = {'script': script, 'div': div,
'script1': script1, 'div1': div1,
'rm_table_one': rm_table_one,
'rm_table_two': rm_table_two}
return render(request, 'base.html', data)
The variables in the data
dict get embedded in the base.html
. The file is below:
<html>
<head>
<link href=”http://cdn.pydata.org/bokeh/release/bokeh-2.3.3.min.css"
rel=”stylesheet” type=”text/css”>
<link href=”http://cdn.pydata.org/bokeh/release/bokeh-widgets-2.3.3.min.css"
rel=”stylesheet” type=”text/css”>
</head>
<body>
<h1>MAP</h1>
{{div| safe}}
</body>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-api-2.3.3.min.js"></script>
{{script| safe}}
<body>
<h1>Latency Bar Plot</h1>
{{div1| safe}}
</body>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.3.3.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-api-2.3.3.min.js"></script>
{{script1| safe}}
<div>
<head>
<style>
.button {
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
.button2 {background-color: #008CBA;} /* Blue */
.button3 {background-color: #f44336;} /* Red */
.button4 {background-color: #e7e7e7; color: black;} /* Gray */
.button5 {background-color: #555555;} /* Black */
</style>
</head>
</div>
<body>
<h2>Data Sources</h2>
<p>Green signified that the data source is alive. Red indicates it is down.</p>
<button class="button">Data Source 1</button>
<button class="button">Data Source 2</button>
<button class="button button3">Data Source 3</button>
<button class="button">Data Source 4</button>
<button class="button">Data Source 5</button>
</body>
<div class="myDiv">
<h1>Redmine Table</h1>
</div>
<div style="width: 260px; float:left; height:100px; margin:10px">
{{rm_table_one| safe}}
</div>
<div style="width: 260px; float:left; height:100px; margin:10px">
{{rm_table_two| safe}}
</div>
</body>
</html>
Do you know of a good way to accomplish this in a private network?
I added the links to the template manually. If there is a better way I would love to hear it.
As listed in the reference guide, Resources
objects have properties js_links
and js_raw
. The js_links
property lists any links that would be need to be configured as src
for <script>
tags in order to load BokehJS (e.g. to the CDN). The js_raw
property has a list of raw JS source strings that would need to be configured inside <script>
tags. INLINE
and CDN
come pre-configured but also include all bundle components (e.g. including the heavyweight mathjax bundle). If you want to create a Resources
object “from scratch” you can limit the components to just what you need:
In [15]: from bokeh.resources import Resources
In [16]: resources = Resources(mode="inline", components=['bokeh','bokeh-widgets'])
In [17]: for x in resources.js_raw: print(f"{x[:50]}\n\n")
/* BEGIN bokeh.min.js */
/*!
* Copyright (c) 2012
/* BEGIN bokeh-widgets.min.js */
/*!
* Copyright
Bokeh.set_log_level("info");
All of three of those scripts would need to go in their own script tags in the template.
Not clear on where I am putting the python you were kind enough to write. Thanks for your patience.
You would pass the values to a Jinja template loop But actually it’s even simpler, I forgot there is a Resources.render()
method. You can just pass the result of that as a single template argument. See
cc @Timo I think this is an area where the docs could be improved. Hardcoding the JS links was the way things had to be done in the beginning. There have been better options to help automate including the right versions for some time, they were just never promoted as much as they probably deserved.
This returns an entire html document. How might one use a similar pattern just to embed plot or dashboards into an existing (django) template while still using the rendering which can be done offline? Thank you in advance.
@wtaylorb I don’t understand your question. The example happens to save an HTML file to disk but that is irrelevant, I linked it because it shows how to pass resources to the template (e.g. by using the result of INLINE.render()
int he template instead of hardcoding them) and that specific part is directly applicable, as-is, to your template code above. Your example code above does not do “offline” rendering, it creates the Bokeh content, calls components
, and renders the template with those values, all in one function. Are you saying your requirements have changed, mid-question??
Sorry for the confusion: I misunderstood what you were suggesting earlier.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.