On Sep 15, 2016, at 3:13 PM, Artur Scholz <[email protected]> wrote:
Ok, I am trying to use bokeh apps then. So I will put all code related to the plot into the app, including data loading and plot updates.
Now, do I need to manually start the bokeh server for this or can this be done from a django view function?
How about the following: User visits the web page (django framework), the view function starts the bokeh server if not already running,
and returns the HTTP response in form of HTML template filled with div & script from the bokeh server generated by the bokeh app?
On Thu, Sep 15, 2016 at 6:29 PM, Bryan Van de Ven <[email protected]> wrote:
Hi,
Unfortunately I can't respond at length, but hopefully a few comments will clear some things up.
It is possible to "prepares the plot and sends it to bokeh server" using "bokeh.client" and "push_session". But I don't recommend this path. Previously the main reason to do this was to afford per-user-session customization. But as of 0.12.2 it is now possible to pass http request arguments to apps that are run directly on the bokeh server (i.e., via "bokeh server myapp.py")
*** I strongly encourage you to write apps in the "bokeh serve myapp.py" instead of using bokeh.client ***
Once you have written an app to run directly on the server, there are two options to "embed it" in a Djanog app:
* an iframe (works great, this is how demo.bokehplots.com works)
* template in the <div> returned by bokeh.embed.autoload_server
Either way, if you need per-session customizations, you can add HTTP request args to the URL for the iframe or for autoload_server, and then the user will see whatever customized version of the app you need to show them.
See:
Bokeh server — Bokeh 3.3.2 Documentation
For more information about accessing HTTP request arguments.
Using "bokeh.client" has a number of intrinsic drawbacks compared to "bokeh serve myapp.py" style apps:
* double the networks traffic (server just becomes a middleman, relaying traffic between python client and browser)
* much harder to scale (need more capacity? just run more "bokeh serve myapp.py" behind a load balancer)
* actually requires more code to write
* client python process or thread must block permanently if there are callbacks to service (session.loop_until_closed)
Thanks,
Bryan
> On Sep 15, 2016, at 11:10 AM, Artur Scholz <[email protected]> wrote:
>
> Thanks, but this is not what I am looking for. Indeed I solved all my needs already using CustomJS callbacks and Ajax polling.
> What I need now is to know how to set up django with bokeh server.
>
> I am confused about the concepts of bokeh document, curdoc, output_server, session, ....
> It should be as simple as:
> - a django view function that prepares the plot and sends it to bokeh server, which in turn is used by the client (via bokehJS) to plot it
> - some other python functions in the view namespace that are used as callbacks for this plot, to react upon range change and to
> receive periodic updates.
>
> Note that preferably the plot creation should be done within the local view function instead of globally (as in all examples I saw), to avoid
> having it running when in fact the website is not even visited.
>
> Yet, I have no idea how to start this properly. Tried several things without success.
>
> Artur
>
> On Thursday, September 15, 2016 at 3:34:44 PM UTC+2, Raffs Walker wrote:
> Hi Artur,
>
> I cannot answer your question. But I can give you something like a workaround/idea.
> I use bokeh with django and I use the approach of CustomJS.
>
> Use the callback function of your zoomtools.
> In the zoom-callback you get the data range from the geometry of the plot.
> with the data range you can update the data of the datasource from django with ajax calls.
>
> Raphael
>
> On 2016-09-15 15:17, Artur Scholz wrote:
>> Hello!
>>
>> Having searched the web and this forum extensively but without success for an answer, here is my challenge:
>>
>> My goal is to plot a schedule of intervals (with attributes: start, end) that are stored in a database and accessed via Django's (ORM) classes.
>>
>> The tricky(?) part is that:
>> a) a line marker shall be drawn on the plot that indicates the current time (and be updated every 10 sec)
>> b) the plot shall initially display 3 days and load data only for this timespan. When user pans the plot, data shall be dynamically loaded and added to the plot (-> range update)
>>
>> I have implemented this with using AjaxDataSource for the periodic time marker update and ColumDataSource with CustomJS for the x_range callback:
>>
>> source = ColumnDataSource()
>>
>> def callback(source=source, window=None):
>> def on_range_update():
>> xhr = window.XMLHttpRequest()
>> data = source.get('data') # the data on the client
>> window_start = int(cb_obj.get('start'))
>> window_end = int(cb_obj.get('end'))
>> buffer_start = data['start'][0]
>> buffer_end = data['end'][-1]
>> url_data_right =
>> url_data_left =
>>
>> if window_start < buffer_start:
>> # append data left
>> url_data_left = "/schedule/api/passes?element_b=1&" + \
>> "start=" + window_start + "&" + \
>> "end=" + buffer_start
>>
>> if window_end > buffer_end:
>> # append data right
>> url_data_right = "/schedule/api/passes?element_b=1&" + \
>> "start=" + buffer_end + "&" + \
>> "end=" + window_end
>>
>> def onreadystatechange():
>> if xhr.readyState == window.XMLHttpRequest.DONE:
>> response = window.JSON.parse(xhr.responseText)
>> if data['start'][-1] <= response['start'][0]:
>> # append data right
>> data['start'] = data['start'] + response['start']
>> data['end'] = data['end'] + response['end']
>> source.trigger('change')
>> else:
>> # append data left
>> data['start'] = response['start'] + data['start']
>> data['end'] = response['end'] + data['end']
>> source.trigger('change')
>>
>> if url_data_right:
>> xhr.onreadystatechange = onreadystatechange
>> xhr.open('GET', url_data_right, False)
>> xhr.send()
>>
>> if url_data_left:
>> xhr.onreadystatechange = onreadystatechange
>> xhr.open('GET', url_data_left, False)
>> xhr.send()
>>
>> if window.range_update_timeout != 0: # cancel the previous update request
>> window.clearTimeout(window.range_update_timeout)
>> window.range_update_timeout = 0
>>
>> window.range_update_timeout = window.setTimeout(on_range_update, 500)
>>
>>
>> def passes_view(request):
>>
>> now = datetime.utcnow()
>> start = now - timedelta(hours=24)
>> end = now + timedelta(hours=24)
>>
>> p1 = figure(x_axis_type='datetime',
>> tools='xwheel_zoom,xpan',
>> active_scroll="xwheel_zoom",
>> )
>>
>> # ensure that plot shows the zoom region
>> border_start = 1000 * calendar.timegm(start.utctimetuple())
>> border_end = 1000 * calendar.timegm(end.utctimetuple())
>> p1.x_range = Range1d(border_start, border_end)
>>
>> # place current time marker
>> source_utc = AjaxDataSource(method='GET',
>> data_url=reverse('api', args=['utc_now']),
>> polling_interval=5000)
>> utc_now = 1000 * calendar.timegm(datetime.utcnow().utctimetuple())
>> source_utc.data = dict(utc_now=[utc_now])
>> p1.quad(source=source_utc, color='red',
>> top=1.5, bottom=-1.5, left='utc_now', right='utc_now')
>>
>> # load passes from database
>> model = Pass
>> qs = model.objects.all()
>> qs = qs.filter(end__gte=start)
>> qs = qs.filter(start__lte=end)
>> qs = qs.filter(element_b=1)
>> qs = qs.order_by('start')
>>
>> qr = [ for i in range(2)]
>> for i in qs:
>> qr[0].append((i.start - datetime(1970,1,1))
>> / timedelta(milliseconds=1))
>> qr[1].append((i.end - datetime(1970,1,1))
>> / timedelta(milliseconds=1))
>>
>> source.data = dict(start=qr[0], end=qr[1])
>> print(source.data)
>> p1.quad(source=source, top=1, bottom=-1, left='start', right='end')
>> p1.x_range.callback = CustomJS.from_py_func(callback)
>>
>>
>> plot = p1
>>
>> script, div = components(plot, INLINE)
>> context = {
>> 'bokeh_script' : script,
>> 'bokeh_div' : div,
>> 'js_resources' : INLINE.render_js(),
>> 'css_resources' : INLINE.render_css()
>> }
>> return render(request, 'schedule/passes_view.html', context)
>>
>>
>>
>> As you can see, the code is quite ugly and things become worse if more plots are involved. So, using a bokeh server from which my Django application can push to/pull from seems to me a solution to this.
>>
>> However, I am missing a roadmap for this.. The bokeh documentation provides examples for bokeh apps, and in the "hapiness" example there is a disclaimer that it is not working with periodic updates (https://github.com/bokeh/bokeh-demos/blob/master/happiness/Building%20happiness.ipynb\)
>>
>> Does someone has a boilerplate code for using bokeh server with django? I imagine that the bokeh server is started from script before starting django server, and then is accessible to all django apps.
>>
>> Thanks
>> Artur
>>
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
>> To unsubscribe from this group and stop receiving emails from it, send an email to bokeh+un...@continuum.io.
>> To post to this group, send email to bo...@continuum.io.
>> To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/cdddb619-4afb-4360-ac48-db44e52b84ad%40continuum.io\.
>> For more options, visit https://groups.google.com/a/continuum.io/d/optout\.
>
>
> --
> You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
> To post to this group, send email to [email protected].
> To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/8484708f-cb19-4b2e-afa5-be04959f6021%40continuum.io\.
> For more options, visit https://groups.google.com/a/continuum.io/d/optout\.
--
You received this message because you are subscribed to a topic in the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this topic, visit https://groups.google.com/a/continuum.io/d/topic/bokeh/1baVw4t6FtY/unsubscribe\.
To unsubscribe from this group and all its topics, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/55ECEB35-50CF-4CA4-A045-E4FA69D352DA%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.
--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/CALqfExZZZk9VXg9JGf7WHCg2XgY2DtoLq1jb%3DLDBN3ivr6KyDA%40mail.gmail.com\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.