Plotly + ajax - plot is not rendered - python

I have a django based web application; and I am using the Python wrappers of plotly to generate plots. When I try to use "ajax style" to fetch only the plot into a <div> </div> with a javascript call no plot is rendered. My situation is as follows:
<head>
<script>
async function generate_plot() {
const url = "/path/to/plot/generator/";
let response = await fetch(url);
let data = await response.text();
let element = document.getElementById('plot_div');
element.innerHTML = data;
}
</script>
</head>
<body>
<div id="plot_div"> </div>
...
</body>
At the /path/to/plot/generator/ endpoint there is a python function which looks like:
def plot_generator(request):
...
return plotly.offline.plot(fig, include_plotlyjs=False, output_type="div")
I am quite certain this is basically sound, because when I manually paste the return value from plot_generator() into <div id="plot_div"></div> the plot is displayed correctly, and also If I let the plot_generator() just return a dummy text like: This should have been a plot - the text is correctly displayed. But the plotly plot is not displayed in any way - the Firefox debug console shows no warnings/errors/anything ...
Something trivially wrong?
Update: When looking in the firefox console the plotly generated content seems to have arrived correctly in the browser/DOM.

Related

Is there a way to plot a plotly chart in a backend server, and to send the interactive results on a webapp?

So, I'm actually making all computations in backend, generate a chart in (.png), save it to a pathfile, and communicate through AJAX the link to this newly generated image. However, such process allows me to transfer an image only. I'm basically converting the plot to an image.
I wonder if there is a way to transfer the entire plotly output, as an interactive chart through AJAX.
import yfinance as yf
import plotly.graph_objects as go
aapl = yf.Ticker('AAPL')
ainfo = aapl.history(start=datemin, end=datemax)
#Plot the Chart
fig = go.Figure(data=go.Scatter(x=ainfo.index,y=ainfo.Close, mode='lines'),)
#DB inject plot
fig.write_image("/Users/Xlibidish/Desktop/Django/static/"+tickerZ+rx+".png")
#And then communicate the path through AJAX etc.
I'd like to send to my Webapp the plotly output. I have some hints:
Generate the plot in my Webapp directly in JS, so the backend sends only the data from yfinance and a directive to generate it. (Quite complex, especially knowing that I have various types of plots, which are all generated in python so the Webappp is only receiving Images at this time without differentiating them).
Create an iframe directing to the plotly output ports, but not sure about this one ! And also, I need to save the plot results in a DB.
Just to clarify:
#in the previous example:
fig.view()
# will be very different from
fig.write_image()
#One will be a png file, the other a pretty cool interactive chart.
```
try fig.to_html()
To be honest, I do not even know what AJAX is. So I am not using AJAX but I still manage to display the entire interactive chart on my "website".
Maybe this gives you some guidance and you can figure out the AJAX part for yourself.
def _give_fig():
data = # input your data here
layout = # input your layout here
fig = go.Figure(data=data, layout=layout) # build your figure with data and layout
fig.update_layout(template='simple_white') # modify your fig until ready
return fig.to_html(config={'displaylogo': False}, include_plotlyjs=False, default_width=790, default_height=600)
# returns chart as html
# displaylogo False to hide the plotly logo in chart
# default width and hight self explanatory
# include_plotlyjs --> important!
def your_view(request):
context = {"your_plot": _give_fig()}
return render(request, 'app/your_template.html', context)
Read more about include_plotlyjs here. You can put it to True and then it will directly include the javascript. It is about 3 mb I think. I personally use the CDN. So have a look at my template:
your_template.html:
{% extends 'base.html' %}
{% block styles %}
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
{% endblock %}
{% block content %}
{% autoescape off %}
{{ your_plot }}
{% endautoescape %}
{% endblock %}
Yeah, that works without the AJAX. Good luck trying to fiddle it in.

Display new graphs in Flask

I have been struggling trying to find a way to update graphs on a flask webserver. I stored the images in the static file of my directory and accessed them by {{url_for('static',filname=ph_plot.png) }} but everytime I would send a post request to fetch a new range of data the graph would not update on my webserver but on my filesystem it would. I know I can change the name of the file everytime I save it to make it appear but I dont know if that is an optimal way to display a dynamically changing photo.
Currently I have been using the send_from_directory method in flask but with it hasnt worked for me either. Below is my code.
I have been working on this for a while and would love some help! Thank you
Notes: all_plots.ph_plot() is calling a function from another python program.
FLASK CODE:
#app.route('/read_ph', methods=["GET", "POST"])
def ph_plot():
if request.method == "POST":
a = request.form['read_ph']
all_plots.ph_plot(a)
time.sleep(3)
ph_plot = os.path.join(os.getcwd(), "images/ph_plot.png")
return render_template('live_stream.html', image_name=ph_plot)
#app.route('/read_ph/<ph_plot>', methods=["GET", "POST"])
def send_ph(ph_plot):
return send_from_directory("images", ph_plot)
HTML:
<html>
<body>
<h1>Data Monitoring Station</h1>
<h2>PH</h2>
<form method="POST" action="read_ph" >
<input name="read_ph" placeholder="Instances" type="text">
</form>
<button type="button">PH Graph</button>
<img src="{{ url_for('send_ph',ph_plot=image_name) }}" id="plot" width ="220" height ="220">
<hr>
<h5> Return to main page RETURN</h5>
<hr>
</body>
</html>
send_from_directory is generally for files that have actually been uploaded into a directory from a user. This is not what you're actually trying to do; you're:
Generating the plot data
Creating a plot and spending time with matplotlib rendering it
Saving this plot image to disk
Loading that image back off disk and sending it to a user
Cut out the middleman here of disk storage: create the data and send it straight to the template. Here's a crude example using plotly.js in a single file to get the data rendered on the front end. You can keep refreshing the page to get different graphs. But note, each plot is interactive; you can zoom, for example, export etc. things with the menu in the top right, show/hide the plot (which would make more sense if there were multiple traces). You don't get any of that by rendering a plot image.
from flask import Flask, render_template_string
import random
app = Flask(__name__)
# Normally you'd have this in the templates directory but for simplicity of
# putting everything into one file for an example, I'm using a template string
template = """
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="plot_div" style="width: 100%; height: 100%"></div>
<script type="text/javascript">
var trace1 = {
x: {{ plot_data.x_axis }},
y: {{ plot_data.y_axis }},
type: 'scatter',
name: 'Example'
};
var layout = {
title: "Test",
titlefont: {
family: 'Poppins',
size: 18,
color: '#7f7f7f'
},
showlegend: true,
xaxis: {
title: 'Axis Unit',
},
yaxis: {
title: 'Other Axis Unit',
},
margin: {
l: 70,
r: 40,
b: 50,
t: 50,
pad: 4
}
};
var data = [trace1];
Plotly.newPlot("plot_div", data, layout);
</script>
"""
def get_plot_data():
x = list(range(10))
y = [random.randint(0, 100) for i in range(10)]
return {'x_axis': x, 'y_axis': y}
#app.route('/')
def home():
plot_data = get_plot_data()
return render_template_string(template,
plot_data=plot_data)
if __name__ == '__main__':
app.run()
A common issue here is in passing datetime strings because they will be escaped. In this case, you'll need to use x: {{ plot_data.x_axis | safe }}. See Passing HTML to template using Flask/Jinja2 and How to make html markup show up?

Refreshing images on a page without refreshing the whole page with Django

I'm making an application that receives images.
I'm making it in a pretty hacky way, where this is the HTML:
<body onload="javascript:setTimeout('location.reload(true);', 1000);" >
<div class="container">
<img class="img0" src="{{ uno }}"/>
<img class="img1" src="{{ dos }}"/>
<img class="img2" src="{{ tres }}"/>
</div>
</body>
And this is in views.py:
def main(request):
x = get_newest_pics()
uno = x[-1]
dos = x[-2]
tres = x[-3]
context = { 'uno' : uno, 'dos' : dos, 'tres' : tres }
return render(request, 'index.html', context)
I'm sure there's a better way to go about this, but I'm very new to Django and I don't know how. At this point, the page is just flickering every second showing the same images, when really I just want it to refresh whenever there is a new image. Is there a way to consistently call get_newest_pics() and refresh just the images, rather than the whole page? Or even just a way to make the page stop flickering?
The way to do this is to implement ajax on your front end, and then request for new images at an interval, once a new image is found, update the container where you are showing your images and add the new (available) image.
Have a look at the django-dajaxice library to help you with the "wiring" of your front end to django correctly.
The way you have written your code, all three images are sent at once to your page, and your javascript snippet is in effect just refreshing the page very quickly, which is why you see the flickering effect.
You could do a "hack" and create a separate view for each image in django, then call each view on an interval using javascript - it would have the same end result but really inefficient in terms of code.

Dynamically update image using Python Flask AJAX

I have 1 very simple web application I am building right now but am very new to flask and jinja (and web development as a whole actually).
I have a watch folder, which will be getting an image sent to it via ftp on a pulse for ever. This wtch folder will only ever have one image in. Every 1 minute, the old image is replaced by a new image, with a new timestamp.
I would like to dynamically update the page, (and displayed timestamp) on a pulse as well, without having to reload any banners or static images that I will add later. I only want to update the following two lines out of the "Channels.Jinja" sample to follow.
<br>{{screenshot_datetime}}<br/>
<img src={{screenshot_location}} width="100%"/>
Channels.Jinja
<!DOCTYPE HTML>
<html>
<head>
<title>Training</title>
</head>
<body bgcolor=white>
<div id=main>
<br>Date and Time of Screenshot <br/>
<br>{{screenshot_datetime}}<br/>
<img src={{screenshot_location}} width="100%"/>
</div>
<div id='test'>
<p>
<script>
var myVar=setInterval(function(){get_image()},1000);
function get_image() {
$.ajax({
type: 'GET',
cache: false,
url: 'get_data',
success: function({{data}}) {
$('img').attr('src', data);
}
});
}
</script>
</p>
</div>
</body>
</html>
Channels.py
def render_channel_route(cr):
static_folder = os.path.join('static',cr)
file_list = os.listdir(static_folder)
channel_files = [f for f in file_list if f.startswith(cr)]
if not channel_files :
logger.error('Could not find image file for Channel. File should start with {0}'.format(cr))
abort(404)
img = os.path.join(static_folder,file_list[0])
ts = get_time_from_filename(file_list[0],cr)
return render_template('Channels.jinja',screenshot_datetime=time.strftime('%c',ts),screenshot_location=img)
#app.route('/channel01-10')
def first_tab():
return render_channel_route('channel01-10')
#app.route('/get_data', methods=['GET'])
def get_data():
return render_template('Channels.jinja',
screenshot_datetime=time.strftime('%c',ts),screenshot_location=img)
Im at a loss, Ive been bumbling around for a while now. Any and all advice is welcome! I am seeing a 304 response upon refresh, but not even the timer i am trying to put on it is working. Pardon sloppy code, highly volatile code is getting changed often -_-
I don't know it there is a "special" way to deal with Ajax using some Flask extension, but in the "normal" Ajax flow first you need to use url_for to put the correct url in your Ajax call and return the data formatted in some way (in my example in JSON) and not to render the template again:
$.ajax({
type: 'GET',
cache: false,
url: "{{ url_for('get_data') }}",
success: function(resp){
$('img').attr('src', resp.url);
$('#sst').html(resp.time);
}
});
So, in your get_data function in your controller you have to get the time and the path again for your image an then return some like this (to fit in my example before):
from flask import json
#app.route('/get_data', methods=['GET'])
def get_data():
#get time and path
time=...
path=...
return json.dumps({time:time,url:path}), 200, {'Content-Type':'application/json'}
Look that I use $('#sst') so you have to put in your HTML:
<br><span id='sst'>{{screenshot_datetime}}</span><br/>

Display multiple mpld3 exports on a single HTML page

I've found the mpld3 package to be brilliant for exporting a matplolib plot to HTML and displaying this via a flask app.
Each export comes with a lot of JS which seems unnecessary duplication if you want to display multiple plots within a single page. However I'm not well enough versed in JS to extract the relevant components and then loop through them. The .fig_to_dict method gives the necessary JSON to display each chart but then I'm left wondering what JS/ template work is needed to display each chart in turn.
I considered just stacking each plot into a single big figure but I'd like to layout the charts in separate DIVs and so this isn't the right solution.
I think I can see what the JS is doing to display them but I don't have enough knowledge to modify the functions to suit the purpose.
I haven't included code as I'm expecting this only to be relevant to someone with mpld3 experience but can supply some sample code if needed.
Sample HTML output for mpld3.fig_to_html(fig, template_type="simple"):
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="http://mpld3.github.io/js/mpld3.v0.1.js"></script>
<style>
</style>
<div id="fig136845463888164451663379"></div>
<script type="text/javascript">
var spec136845463888164451663379 = { <snip JSON code> };
var fig136845463888164451663379 = mpld3.draw_figure("fig136845463888164451663379", spec136845463888164451663379);
</script>
I'd thought it would be as simple as linking the two core scripts from the template header and then creating a new script for each JSON export. But that hasn't worked for me.
You're half-way there with your answer. I think what you want to do is something like this, which will embed three figures on the page:
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="http://mpld3.github.io/js/mpld3.v0.1.js"></script>
<style>
</style>
<div id="fig01"></div>
<div id="fig02"></div>
<div id="fig03"></div>
<script type="text/javascript">
var json01 = { <snip JSON code> };
var json02 = { <snip JSON code> };
var json03 = { <snip JSON code> };
mpld3.draw_figure("fig01", json01);
mpld3.draw_figure("fig02", json02);
mpld3.draw_figure("fig03", json03);
</script>
The json code for each figure can be created in Python by running
import json
# ... create matplotlib figure
json01 = json.dumps(mpld3.fig_to_dict(fig))
Embed this string at the appropriate place in the HTML document you're creating, and you should be good to go. I hope that helps!
Note that since jakevdp's answer was posted mpld3 has had a new release. As of today (September 2014) the mpld3 include has to be:
<script type="text/javascript" src="http://mpld3.github.io/js/mpld3.v0.2.js"></script>

Categories