I am trying to create a bokeh plot in a Django app to graph swimming events for an athlete. I am plotting duration (time swam) vs date (date of swim meet). The idea is to have one plot and be able to use a SelectBox widget to choose which event is shown on the graph. The problem is that when I change the source of the data in a CallbackJS function, the graph does not update and instead goes blank.
The data comes from Event objects of the form
class Event(models.Model):
swimmer = models.ForeignKey(Swimmer, on_delete=models.SET_NULL, null=True, blank=True)
team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=50, null=True, blank=True)
gender = models.CharField(max_length=1, choices=GENDER_CHOICE, null=True, blank=True)
event = models.CharField(max_length=10, choices=EVENT_CHOICE)
time = models.DurationField()
place = models.IntegerField(null=True, blank=True)
date = models.DateField(null=True)
First, I iterate through each of the events (17 currently) and makes a list of the date (datetime.date) and time (datetime.timedelta) fields. The unmodified fields are used for x and y respectively, and the values are edited slightly (mostly type cast to string) to be used for the hover tool. If there is no data for the particular event, the data_source{} entry is set to None.
Example data:
data_source = {
'x_50_free': [date(2017,9,7), date(2017,9,8)]
'y_50_free': [timedelta(seconds=22.96), timedelta(seconds=22.32)]
'date_50_free': ['9/7/2017', '9/8/2017']
'time_50_free': ['00:22.96', '00:22.32']
'x_100_free': [date(2017,9,7)]
'y_100_free': [timedelta(seconds=49.86)]
'date_100_free': ['9/7/2017']
'time_100_free': ['00:49.86']
}
Then, I plot an initial line so that a line is displayed when the page loads.
source = ColumnDataSource(data=dict(
x=data_source['x_'+first_event],
y=data_source['y_'+first_event],
date=data_source['date_'+first_event],
time=data_source['time_'+first_event]
))
plot.line('x', 'y', source=source)
I update the source data in my callback function
callback = CustomJS(args=dict(source=source), code="""
data = %s;
f = cb_obj.value;
if (f == "50 Freestyle") {
source.data['x'] = data.x_50_free;
source.data['y'] = data.y_50_free;
source.data['date'] = data.date_50_free;
source.data['time'] = data.time_50_free;
} else if (f == "100 Freestyle") {
source.data['x'] = data.x_100_free;
source.data['y'] = data.y_100_free;
source.data['date'] = data.date_100_free;
source.data['time'] = data.time_100_free;
}
...
} else if (f == "400 IM") {
source.data['x'] = data.x_400_im;
source.data['y'] = data.y_400_im;
source.data['date'] = data.date_400_im;
source.data['time'] = data.time_400_im;
}
source.change.emit();
""" % json.dumps(data_source, cls=DatetimeEncoder)) # encoder to handle datetimes for x-axis
From what I understand, source.change.emit() is used to update the ColumnDataSource. This seems to work as I am able to log source.data[] to the console and see it update depending on the Select widget option, but the plot itself does not update it simply goes blank. How do I also update the plot to reflect the change in source data?