Using AjaxDataSource to read online data sources in stand-alone html deployment

I’m trying to put together a standalone .html bokeh output which uses an external datasource (hosted online, and updated annually using the same filename), and have been playing around with AjaxDataSource to do this. I’ve been trying to do this with the Iris dataset as practise, and have saved it in the JSON format and parsed it through AjaxDataSource, but it doesn’t seem to be working. When I parse print(source.data) it only returns {}.

I’m quite new to python and bokeh, but have been working on this for a while and haven’t made any progress. Any help would be really greatly appreciated! Alternately, if there is an easier way to achieve this goal please let me know!

from bokeh.io import curdoc, show, output_file, output_notebook
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool, CategoricalColorMapper, AjaxDataSource, CustomJS
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.palettes import Spectral6
import seaborn as sns
import pandas as pd
output_notebook()

adapter = CustomJS(code="""
    const result = {sepal_length: [], sepal_width: [], species: []}
    const s_l = cb_data.response.sepal_length
    const s_w = cb_data.response.sepal_width
    const s_p = cb_data.response.species
    for (let i=0; i<s_l.length; i++) {
        result.sepal_length.push(s_l[i])
        result.sepal_width.push(s_w[i])
        result.species.push(s_p[i])
    }
    return result
""")

# AjaxDataSource using the JSON dictionary format of the iris dataset
source = AjaxDataSource(data_url='http://rawcdn.githack.com/guildgensec/livedatadashboard/f3a60aa27921371365ef7567db69851dea721fff/iris.json',adapter=adapter)


# Import the Iris dataset (as pandas dataframe) to use for calculating axis, then find categories of iris and set axis
irisforheadings=sns.load_dataset("iris")

species_list = irisforheadings.species.unique().tolist()

xmin, xmax = min(irisforheadings.sepal_length), max(irisforheadings.sepal_length)
ymin, ymax = min(irisforheadings.sepal_width), max(irisforheadings.sepal_width)

# Establish color mapping using categories
color_mapper = CategoricalColorMapper(factors=species_list,palette=Spectral6)

p = figure(title = 'Irises', plot_height = 400, plot_width= 700, x_range=(xmin,xmax),y_range=(ymin,ymax))

p.circle(x='sepal_length',y='sepal_width',fill_alpha=0.8, color=dict(field='species', transform = color_mapper), legend='species', source=source)

p.xaxis.axis_label = 'Sepal Length (cm)'
p.yaxis.axis_label = 'Sepal Width (cm)'
p.legend.location = 'top_right'
show(p)

Thanks again for your time!

@mottlecah AjaxDataSource updates are something that happens only in the browser. There is no work (i.e. data loading) that is done in the Python process at all. There will not be any data loaded until the Bokeh content is rendered in the page, and then the request to the data_url is made from JavaScript.

But there is another issue, which is that AjaxDataSource will probably not work against a static CDN url. If you run your code and look at the browser’s JavaScript console, you will probably see an error message like:

XMLHttpRequest cannot load http://rawcdn.githack.com/guildgensec/livedatadashboard/f3a60aa27921371365ef7567db69851dea721fff/iris.json due to access control checks.

It’s possible there is some combination of headers and method that will permit this to work. I don’t know, it depends on the CDN specifically, and what it is configured to allow (i.e. nothing we can control). FWIW the AjaxDataSource is really intended to hit actual live REST API endpoints on a server, not a static CDN / file server.

1 Like