how to embed standalone bokeh graphs into django templates - django

I want to display graphs offered by the bokeh library in my web application via django framework but I don't want to use the bokeh-server executable because it's not the good way. so is that possible? if yes how to do that?

Using the Embedding Bokeh Plots documentation example as suggested by Fabio Pliger, one can do this in Django:
in the views.py file, we put:
from django.shortcuts import render
from bokeh.plotting import figure
from bokeh.resources import CDN
from bokeh.embed import components
def simple_chart(request):
plot = figure()
plot.circle([1,2], [3,4])
script, div = components(plot, CDN)
return render(request, "simple_chart.html", {"the_script": script, "the_div": div})
in the urls.py file we can put :
from myapp.views import simple_chart
...
...
...
url(r'^simple_chart/$', simple_chart, name="simple_chart"),
...
...
in the template file simple_chart.html we'll have :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Experiment with Bokeh</title>
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-0.8.1.min.js"></script>
<link rel="stylesheet" href="http://cdn.bokeh.org/bokeh/release/bokeh-0.8.1.min.css">
</head>
<body>
{{ the_div|safe }}
{{ the_script|safe }}
</body>
</html>
And it works.

You don't need to use bokeh-server to embed bokeh plots. It just means you'll not be using (and probably don't need) the extra features it provides.
In fact you can embed bokeh plots in many ways like generating standalone html, by generating bokeh standalone components that you can then embed in you django app when rendering templates or with the method we call "autoloading" which makes bokeh return a tag that will replace itself with a Bokeh plot. You'll find better details looking at the documentation.
Another good source of inspiration is the embed examples you can find in the repository.

It is also possible to have it work with AJAX requests. Let's say we have a page loaded and would like to show a plot on button click without reloading the whole page. From Django view we return Bokeh script and div in JSON:
from django.http import JsonResponse
from bokeh.plotting import figure
from bokeh.resources import CDN
from bokeh.embed import components
def simple_chart(request):
plot = figure()
plot.circle([1,2], [3,4])
script, div = components(plot, CDN)
return JsonResponse({"script": script, "div": div})
When we get AJAX response in JS (in this example Jquery is used) the div is first appended to the existing page and then the script is appended:
$("button").click(function(){
$.ajax({
url: "/simple_chart",
success: function(result){
var bokeh_data = JSON.parse(result);
$('#bokeh_graph').html(bokeh_data.div);
$("head").append(bokeh_data.script);
}});
});

It must put {{the_script|safe}} inside the head tag

Here's a flask app that uses jquery to interract with a bokeh plot. Check out the templates/ for javascript you can reuse. Also search for bokeh-demos on github.

Related

Print Barcode in PDF with Django

I am using render_to_string in django for parse an HTML and export to PDF.
html = render_to_string("etiquetaTNT.html", {
'context': context,
'barcode': b,
'barcodeimg': barcodeimg,
})
font_config = FontConfiguration()
HTML(string=html).write_pdf(response, font_config=font_config)
return response
I am trying to insert a barcode in PDF. I generate this barcode in a PNG.
br = barcode.get('code128', b, writer=ImageWriter())
filename = br.save(b)
barcodeimg = filename
But the PDF in template, not show the image.
<img class="logo" src="{{barcodeimg}}" alt="Barcode" />
I do not know the way to save the filename in the template that I want, and I do not know to show in the PDF, because any image is showed. For example, the logo, it is showed in HTML template but not in the PDF.
<img class="logo" src="{{logo}}" alt="TNT Logo" />
The libraries that I am using:
import barcode
from barcode.writer import ImageWriter
from django.http import HttpResponse
from django.template.loader import render_to_string
from weasyprint import HTML
from weasyprint.fonts import FontConfiguration
I do not want to use Reportlab, because I need to render a HTML, not a Canvas.
Understanding the problem:
Think about what happens when you load a webpage. There is the initial request where the document is loaded, and then subsequent requests are made to fetch the images / other assets.
When you want to print some HTML to PDF using weasyprint, weasyprint has to fetch all of the other images. Checking out the python-barcode docs, br.save(b) is just going to return literally just the filename, (which will be saved in your current working directory). So your html will look something like this:
<img class="logo" src="some_filename.svg" alt="Barcode" />
Quite how it fetches this will depend on how you have weasyprint set up. You can check out django-weasyprint which has a custom URL fetcher. But as things stand, weasyprint can't fetch this file.
A solution
There are a few ways you can fix this. But it depends alot on how you are deploying this. For example, heroku (as I understand it) doesn't have a local file system you can write to, so you would need to write the file to an external service like s3, and then insert the url for that into your template, which weasyprint will then be able to fetch. However, I think there is probably a simpler solution we can use in this case.
A better (maybe) Solution
Taking a look at the python-barcode docs it looks like you can write using SVG.
This is good because we can insert SVG straight into our HTML template (and avoid having to fetch any other assets). I would suggest something like the following
from io import BytesIO
from barcode.writer import SVGWriter
# Write the barcode to a binary stream
rv = BytesIO()
code = barcode.get('code128', b, writer=SVGWriter())
code.write(rv)
rv.seek(0)
# get rid of the first bit of boilerplate
rv.readline()
rv.readline()
rv.readline()
rv.readline()
# read the svg tag into a string
svg = rv.read()
Now you'll just need to insert that string into your template. Just add it to your context, and render it as follows:
{{svg}}
Enhancing the solution provided by #tim-mccurrach, I have created a templatetag for it.
/app/templatetags/barcode_tags.py
from django import template
from io import BytesIO
import barcode
register = template.Library()
#register.simple_tag
def barcode_generate(uid):
rv = BytesIO()
# code = barcode.get('code128', b, writer=SVGWriter())
code = barcode.get('code128', uid,
writer=barcode.writer.SVGWriter())
code.write(rv)
rv.seek(0)
# get rid of the first bit of boilerplate
rv.readline()
rv.readline()
rv.readline()
rv.readline()
# read the svg tag into a string
svg = rv.read()
return svg.decode("utf-8")
And then In the template.html:
{% load barcode_tags %}
{% barcode_generate object.uid as barcode_svg %}
{{barcode_svg | safe}}

Changing the favicon in Flask/Dash

Trying to get the favicon to load I have followed suggestions from the internet:
server = Flask(__name__, static_folder='static')
app = dash.Dash(external_stylesheets=external_stylesheets, server=server)
app.css.config.serve_locally = False
app.scripts.config.serve_locally = True
#server.route('/favicon.ico')
def favicon():
print('Server root path', server.root_path)
return send_from_directory(os.path.join(server.root_path, 'static'),
'dice.ico', mimetype='image/vnd.microsoft.icon')
...
app.run_server(debug=True)
If I browse to the favicon, I see it:
http://www.example.com/favicon.ico
However, when I browse to
http://www.example.com
I see the dash default icon with it's own description. How do I ensure my ownfavicon loads correctly?
To simply change the favicon all you need to do is to create a folder called assets next to your app.py and place your favicon.ico inside of that folder and it will work perfectly.
app.py:
import flask
import dash
import dash_html_components as html
server = flask.Flask(__name__)
#server.route('/')
def index():
return 'Hello Flask app'
app = dash.Dash(
__name__,
server=server,
routes_pathname_prefix='/dash/'
)
app.layout = html.Div("My Dash app")
if __name__ == '__main__':
app.run_server(debug=True)
Here is the docs link for more information: Dash docs
An alternative used in Dash is:
app = dash.Dash()
app._favicon = ("path_to_folder/(your_icon).co")
You can just put title="My Title" as an argument when establishing the Dash app instance. i.e.
app = dash.Dash(
__name__,
title="My Title",
server=server,
routes_pathname_prefix='/dash/'
)
Adding for the sake of completeness. Nowadays, it's recommended to use a bundle of icons with different resolutions to please different browsers and ensure the best picture quality. You can produce such a bundle with the help of, say, realfavicongenerator.net Or you simply might want to use a non-standard .png or .svg icon.
For that, you can subclass Dash and add your much desired link rel and meta tags to its interpolate_index method:
import dash
class CustomDash(dash.Dash):
def interpolate_index(self, **kwargs):
return '''
<!DOCTYPE html>
<html>
<head>
{metas}
<title>{title}</title>
<link rel="apple-touch-icon" sizes="180x180" href="assets/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/favicons/favicon-16x16.png">
<link rel="manifest" href="assets/favicons/site.webmanifest">
<link rel="mask-icon" href="assets/favicons/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
{css}
</head>
<body>
{app_entry}
<footer>
{config}
{scripts}
{renderer}
</footer>
</body>
</html>
'''.format(**kwargs)
app = CustomDash()
Do not forget to place the unzipped bundle into your assets/favicons subfolder!
You should create an "assets" folden and then update the "__favicon" property in your Dash app:
app._favicon = "favico.ico"
Even if OP's accepted answer doesn't work, refer to official document which says:
It is recommended to add name to the dash init to ensure the resources in the assets folder are loaded, eg: app = dash.Dash(name, meta_tags=[...]). When you run your application through some other command line (like the flask command or gunicorn/waitress), the main module will no longer be located where app.py is. By explicitly setting name, Dash will be able to locate the relative assets folder correctly.
Include __name__ in Dash object:
app = Dash(__name__)

bokeh model does not exist

I have django bokeh project and I'm trying to add dateRangePicker slider but I get error in console
bokeh-0.12.16.min.js:1 Uncaught Error: Model 'DateRangeSlider' does not exist. This could be due to a widget
or a custom model not being registered before first usage.
This is code for that.
date_range_slider = DateRangeSlider(title="Date Range: ", start='2018-01-02', end='2018-06-09',
value=('2018-06-02', '2018-06-09'), step=1)
# l = layout(children=[[date_range_slider]], sizing_mode='fixed')
l = layout(children=[[p], [date_range_slider]], sizing_mode='fixed')
script, div = components(l)
print(div)
return render(request, 'index.html', {"the_script": script, "the_div": div})
Do I need to add anything in django model?
Update: note that starting with Bokeh 2.0, there are only JS components to load (no separate CSS files)
BokehJS is split into multiple pieces so that users who do not need, e.g. widgets, do not have to load the extra JS and CSS for them. If you use widgets and components, you need to explicitly include the extra JS and CSS for them as described in the documentation
<link
href="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-x.y.z.min.css"
rel="stylesheet" type="text/css">
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-x.y.z.min.js"></script>

Dygraph is not defined, using Flask

I am using flask and trying to get the dygraph sample to work. Here is the sample code (2nd example from the tutorial page: http://dygraphs.com/tutorial.html):
<html>
<head>
<script type="text/javascript"
src="dygraph-combined.js"></script>
</head>
<body>
<div id="graphdiv2"
style="width:500px; height:300px;"></div>
<script type="text/javascript">
g2 = new Dygraph(
document.getElementById("graphdiv2"),
"temperatures.csv", // path to CSV file
{} // options
);
</script>
</body>
</html>
Here is my Flask code (I'm trying to use render_template()):
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def render_plot():
return render_template('sample.html')
if __name__ == '__main__':
app.run()
When I run this through python firebug gives me the error "Dygraph is not defined." from the line g2 = new Dygraph(
Sample.html works in my folder, but it does not work when I try to access it from my url after running my flask code from python. My folders look like this:
FlaskStuff/main.py
FlaskStuff/templates/sample.html
FlaskStuff/templates/dygraph-combined.js (To load sample.html in my folder).
FlaskStuff/js/dygraph-combined.js
I am new to Flask. Similar answers did not help me to solve this problem.
Where is dygraph-combined.js located? It needs to be somewhere it can be served. You will most likely what to place it inside your static folder. It's a fairly common practice to group like files inside static (e.g., css, js).
Using this structure
static/
js/
dygraph-combined.js
you'll want to update sample.html as follows
<script type="text/javascript" src="{{ url_for('static', filename='js/dygraph-combined.js') }}"></script>
This will allow the Flask development server to serve the file. You'll also want to add a rule to your HTTP server to serve content from /static directly.
You will also have to do a similar thing for the temperature.csv file. (I've placed it in the static folder)
"{{ url_for('static', filename='temperatures.csv') }}", // path to CSV file

How to Connect JQuery-UI Autocomplete Widget to Django-Dajax?

I have a Django app for which I am trying to build an autocomplete UI for making selections. As the user types into a text box and the the app should make search suggestions based on values retrieved from the database. I want to use Dajax/Dajaxice to handle the ajax and the jquery-ui autocomplete widget for the UI template. Can someone please explain how to get the jquery-ui autocomplete widget to call my dajax function via the autocomplete source attribute (or any other better way)?
My code is a combination of this dajax example and this jquery-ui autocomplete example.
my_app/ajax.py:
from my_app.models import MyModel
from dajax.core import Dajax
from dajaxice.decorators import dajaxice_register
from django.utils import simplejson
#dajaxice_register
def auto_filter_selections(request, symbol):
dajax = Dajax()
result = MyModel.objects.filter(symbol__startswith = symbol)
dajax.assign('#id_symbol', 'innerHTML', str(result))
return dajax.json()
template: my_app_ui.html
<head>
....
<script type="text/javascript" src="{{STATIC_URL}}jquery/jquery-1.7.2.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}dajax/jquery.dajax.core.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}/jquery/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.js"></script>
....
<script>
$(function() {
$("#id_symbol").autocomplete({
source: "Dajaxice.ui.auto_filter_symbols(Dajax.process,'symbol':$('#id_symbol').serialize(true)});",
minLength: 1 //We want to search for even one character
});
});
</script>
....
</head>
<body>
....
<div class="ui-widget">
<label for="id_symbol">Symbol: </label>
<input id="id_symbol">
</div>
....
</body>
If you notice above, I am using jquery-1.7.2 and jquery-ui-1.10.4. This is because the dajax documentation says it is compatible with jquery-1.7.2. Not sure if I can use a newer jquery version.
I am not sure how to get the template's javascript to call my dajax function. the jquery-ui documentation for .autocomplete says to use its source attribute but does not give a very good example. Can anyone tell me if the above is correct?
Once the dajax view function retrieves suggestions from the DB, how do I populate the .autocomplete text box with it?
I've done quite a bit of research over the past few days but there are few examples of Django-Dajax-JQueryUI applications out there.
After considerably more research and piecing various clues together I finally found the key to getting the JQuery-UI autocomplete widget to work with Dajax/Dajaxice and Django.
Synopsis:
Initialize JQuery-UI .autocomplete's source option with a separate javascript (JQuery) setter function
Connect .autocomplete widget's source option to server-side function with a Dajaxice command via a third javascript function.
Explanation:
Firstly, it is important to remember that Dajaxice provides the ajax communication channel by which your client-side javascript and the server side code transact, whereas Dajax provides a convenient way of generating the javascript (JSON) code that will perform the desired function in the client browser without having to write any javascript.
Since JQuery-UI provides us with the javascript widget (in the form of jquery) to perform our desired function (.autocomplete), we do not need Dajax and will not, according to my findings, have any success with it in this application. We ONLY need Dajaxice in order to connect the client side javascript (.autocomplete) to our server side function that does the searching and returns results to suggest to the user.
Below is my revised code that I have gotten working. Note that I have only tested it on a development platform thus far.
my_autocomplete_search_template.html with extraneous code removed:
<head>
....
<script type="text/javascript" src="{{STATIC_URL}}jquery/jquery-1.7.2.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}dajax/jquery.dajax.core.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}/jquery/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.js"></script>
....
<script>
//Initialize the source option of .autocomplete widget during page load
//See: http://api.jqueryui.com/autocomplete/#method-option setter example code
var local_source = ["Abc",] //Dummy string for initialization only; temporary value
$("#search_box").autocomplete("option", "source", local_source)
</script>
<script>
//.autocomplete widget is Dajaxice callback function
// *data* comes from server-side function
function search_result(data) {
$("#search_box").autocomplete({
source: data,
});
}
</script>
<script>
//This function called by user <input> tag and calls the server-side function via Dajaxice command
function dajax_filter_search_term() {
Dajaxice.my_app.auto_filter_search_term(search_result,'search_term':$('#search_box').val()});
// | | | ----------------------
// | | | |
// server-side .autcomplete serverside user-typed
// function callback func. func. argument value to search
}
</script>
....
</head>
<body>
....
<div class="ui-widget">
<label for="search_box">Search Term: </label>
<input type="text" name="search" id="search_box" onkeyup="dajax_filter_search_term()" />
</div>
....
</body>
my_app/ajax.py
from dajaxice.decorators import dajaxice_register
from django.utils import simplejson
from my_app.models import MyModel
#dajaxice_register
def auto_filter_search_term(request, search_term):
# Query DB for suggestions to present to user
q = MyModel.objects.filter(myfield__istartswith=search_term)
data = []
for item in q:
# Format returned queryset data, if needed
data_item = item.field1 + ', ' + item.field2 + ', ' + item.field3
data.append(data_item)
# Return data to callback function *search_result*
return simplejson.dumps(data)
That is it! The key is to initialize the .autocomplete source option first and to only use Dajaxice to communicate with the server-side function.
It is possible to optimize the client-side code further by getting rid of the javascript function dajax_filter_search_term() and including the Dajaxice call in the #search_box input tag like so:
<input type="text" name="search" id="search_box" onkeyup="Dajaxice.ui.auto_filter_search_term(search_result, {'search_term':$('#search_box').val()})" />
References
JQuery-UI API docs: http://api.jqueryui.com/autocomplete/#method-option:
Additional clues here:
Django, JQuery, and autocomplete