Flask Plotly Responsive in Browser - javascript

How to make this plotly chart responsive in index.html - I create a flask app and js function for rendering the plot, however, I cannot figure out, how to make it responsive. What is wrong with variable config - it seems not to work.
Thank you
Flask:
#app.route('/')
def index():
locations = sorted(df['location'].unique())
rooms = sorted(df['rooms'].unique())
# Visualisation
import json
import plotly
import plotly.express as px
df.rooms = df.rooms.astype(str)
fig = px.scatter(df, x="m2", y="price", trendline="ols", color="rooms", symbol='rooms',
marginal_x="histogram",
marginal_y="rug",
template='plotly_dark', hover_data=['price', 'rooms', 'm2', 'location'],
title="Real Estate in Vienna")
graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
return render_template('index.html', locations=locations, rooms=rooms, graphJSON=graphJSON)
JavaScript:
<!-- Visualisation -->
{%block content%}
<div class="card mb-4 m-auto" style="width: 90%">
<div class="card-body">
<div id="chart"></div>
</div>
</div>
{% endblock %}
<!-- Plotly -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<script type="text/javascript">
var graphs = {{ graphJSON | safe}};
var config = {responsive: true};
Plotly.newPlot("chart", graphs, {}, config);
</script>

I think you are using Plotly.newPlot() wrong. By reading the API reference you will see, that there are to different signatures for this function:
Plotly.newPlot(graphDiv, data, layout, config)
Plotly.newPlot(graphDiv, obj)
Looks you were mixing both up since the Python function
graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
returns a single object with keys for data, layout, config and frames with defaults to {data: [], layout: {}, config: {}, frames: []}.
Thus everything should work if you use the following JavaScript that extracts the parameters data and layout from the object charts and then use
Plotly.newPlot(graphDiv, data, layout, config)
instead of
Plotly.newPlot(graphDiv, obj):
<!-- Plotly -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<script type="text/javascript">
var chart = {{graphJSON | safe}};
var data = chart["data"];
var layout = chart["layout"];
var config = {responsive: true};
Plotly.newPlot("chart", data, layout, config);
</script>

Related

Displaying D3.js Chart with Django Backend

I am learning to build dashboard using Django as backend and D3.js for visualization.
Following is my index.html:
{% load static %}
<html>
<script src="https://d3js.org/d3.v7.min.js"></script>
<body>
<h1> Hello! </h1>
<script src={% static "js\linechart.js" %}>
var data = {{ AAPL|safe }};
var chart = LineChart(data, {
x: d => d.date,
y: d => d.close,
yLabel: "↑ Daily close ($)",
width,
height: 500,
color: "steelblue"
})
</script>
</body>
</html>
Data AAPl is extracted from database and the views.py is as follows:
from django.shortcuts import render
from django.http import HttpResponse
from cnxn import mysql_access
import pandas as pd
# Create your views here.
def homepage(request):
sql = ''' select Date, Close from tbl_historical_prices where ticker = 'AAPL' '''
cnxn = mysql_access()
conn = cnxn.connect()
df = pd.read_sql(sql, con=conn)
context = {'AAPL':df.to_json()}
return render(request, 'index.html', context=context)
Function line chart can be viewed here which is being used in js\linechat.js in index.html file.
I can see the Hello! being displayed on the page but can't see the line chart. I am unable to debug the problem. No errors found in console tab either.
How can I display the line plot?
I've added the current page display in attached image.
Close off your script with a src and start a new script tag. The presence of src precludes internal code.
<script src={% static "js\linechart.js" %}></script>
<script>
...

I'm facing a TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement

Hi I'm trying to create a horizontal bar chart using flask and facing the following error. TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement. Please help. Thanks in advance.
app.py
from flask import Flask, redirect, url_for, render_template, request, flash, session
import pandas as pd
app = Flask(name)
app.debug = True
#app.route('/', methods=['GET','POST'])
def index():
return render_template('index.html')
#app.route('/data', methods=['GET','POST'])
def dropdown():
if request.method == 'POST':
file = request.form['upload-file']
data = pd.read_excel(file)
data = pd.DataFrame(data)
colours=data['Question'].unique().tolist()
return render_template('test.html', colours=colours)
#app.route("/data/submitted", methods=['GET', 'POST'])
def charts():
#select = request.form.get('colour')
if request.method == 'POST':
to_filter = request.form.get['colours']
# filter the data here
plot_data = data[data['Themes'].str.contains(to_filter)]
plot_data['flag'] = 1
plot_data2 = plot_data.groupby(['Themes'])['flag'].sum().reset_index()
#colours1 = data1['Question'].unique().tolist()
labels = plot_data2['Themes'].tolist()
values = plot_data2['flag'].tolist()
bar_labels = labels
bar_values = values
return render_template("sample_chart1.html", labels=bar_labels,data=bar_values)
if __name__ == "__main__":
app.run(debug=True)
sample_chart1.html
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<head>
<meta charset="utf-8" />
<title>Charts.js</title>
<!-- import plugin script -->
</head>
<h1>Chart</h1>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<!-- bar chart canvas element -->
<canvas id="chart" width="300" height="200"></canvas>
<p id="caption">Distribution of Topics.</p>
<script>
// create the chart using the chart canvas
var chartData = {
labels : [{% for item in labels %}
"{{item}}",
{% endfor %}],
datasets : [{
data : [{% for item in values %}
{{item}},
{% endfor %}],
spanGaps: false
}]
}
// get chart canvas
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: 'horizontal bar',
data: chartData,
});
</script>
It is making a GET request, which does not pass the if request.method == 'POST'
And since it does not pass there is no return for requests that aren't POST

How to autorefresh SVG content in flask

I am trying to make auto filling graph with pygal on flask, I have tried the setInterval from jQuery in Javascript and text values are refreshing correctly, but still can't make autorefresh of the graph. Here are my sources:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type=text/javascript>
setInterval(
function()
{
$.getJSON(
'/_data',
{},
function(data)
{
$("#x-value").text('X: '+data.xval);
$("#y-value").text('Y: '+data.yval);
$("#z-value").text('Z: '+data.zval);
$("#chart").text(data.chart)
});
},
500);
</script>
...
<div id=x-value>X</div>
<div id=y-value>Y</div>
<div id=z-value>Z</div>
</div>
<div class="col-lg-6">
<object id="chart" type="image/svg+xml"></object>
</div>
I have tried many combinations of the "Object" and div tags, nothings's working..
Here is my python:
def data():
data = get_last()
charts = chart()
return jsonify(xval=data['X'],
yval=data['Y'],
zval=data['Z'],
chart=charts)
def chart():
data = get_last_hun()
chart = line_route(data)
return chart
def get_last():
vals = db.get_last()
return vals
def get_last_hun():
arr = {'X':[],'Y':[],'Z':[]}
latest = db.get_last()['id']
start = 1
if latest > 100:
start = latest-100
while start<=latest:
arr['X'].append(db.get_val(start)['X'])
arr['Y'].append(db.get_val(start)['Y'])
arr['Z'].append(db.get_val(start)['Z'])
start = start + 1
return arr
def line_route(data):
chart = pygal.Line()
chart.add('X', data['X'])
chart.add('Y', data['Y'])
chart.add('Z', data['Z'])
chart.render(is_unicode=True)
return render_template('home.html',chart = chart)
Do you know maybe some other way how to make it working, please?
Thank you very much!

Embedding multiple pieces of JavaScript via a Tornado UIModule

I'm working on a Python package that uses Tornado to send data to the browser for visualization. In order to do this, I want the users to be able to write multiple arbitrary modules for the server to render together on a single page -- including each module's own JavaScript.
However, by default, the Tornado's UIModule class's embedded_javascript() method only appends JavaScript to <script>...</script> once per module class. I'm hoping there is a simple way to embed multiple pieces of JS, one for every UIModule (or another way to get the same effect).
Here's a minimal example of what I'm talking about:
import tornado.ioloop
import tornado.web
import tornado.template
class Element(tornado.web.UIModule):
'''
Module to add some custom JavaScript to the page.
'''
def render(self, element):
self.js_code = element.js_code
return ""
def embedded_javascript(self):
return self.js_code
class InterfaceElement(object):
'''
Object to store some custom JavaScript code.
'''
def __init__(self, js_code):
'''
Args:
js_code: Some JavaScript code in string form to add to the page.
'''
self.js_code = js_code
class MainPageHandler(tornado.web.RequestHandler):
def get(self):
elements = self.application.modules
self.render("uitest_template.html", app_name="Testing", elements=elements)
class ThisApp(tornado.web.Application):
def __init__(self, modules):
self.modules = modules
main_handler = (r'/', MainPageHandler)
#settings = {"ui_modules": {"Element": Element}}
settings = {"ui_modules": {"Element": Element},
"template_path": "ui_templates"}
super().__init__([main_handler], **settings)
# Create two objects with some custom JavaScript to render
module_1 = InterfaceElement("var a = 1;")
module_2 = InterfaceElement("var b = 2;")
app = ThisApp([module_1, module_2])
app.listen(8888)
tornado.ioloop.IOLoop.instance().start()
And the template for uitest_template.html is just
<!DOCTYPE html>
<head>
<title> Hello World </title>
</head>
<body>
{% for element in elements %}
{%module Element(element) %}
{% end %}
</body>
The rendered page then includes a <script> tag in body that is:
<script type="text/javascript">
//<![CDATA[
var b = 2;
//]]>
</script>
And what I want is:
<script type="text/javascript">
//<![CDATA[
var a = 1;
var b = 2;
//]]>
</script>
Or something like it. Any ideas?
Added - my solution
Based on the answer below, here's how I ended up handling it:
import tornado.ioloop
import tornado.web
import tornado.template
class InterfaceElement(object):
include_js = [] # List of .js files to include
js_code = '' # JavaScript string to include
def __init__(self, include_js=[], js_code=''):
self.include_js = include_js
self.js_code = js_code
class MainPageHandler(tornado.web.RequestHandler):
def get(self):
self.render("modular_template.html",
includes=self.application.include_js,
scripts=self.application.js_code)
class ThisApp(tornado.web.Application):
def __init__(self, modules):
# Extract the relevant info from modules:
self.modules = modules
self.include_js = set()
self.js_code = []
for module in self.modules:
for include_file in module.include_js:
self.include_js.add(include_file)
if module.js_code != '':
self.js_code.append(module.js_code)
main_handler = (r'/', MainPageHandler)
settings = {"template_path": "ui_templates",
"static_path": "ui_templates"}
super().__init__([main_handler], **settings)
module_1 = InterfaceElement(js_code="var a = 1;")
module_2 = InterfaceElement(include_js=["test.js"], js_code="var b = 1;")
app = ThisApp([module_1, module_2])
app.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Which goes with the following template:
<!DOCTYPE html>
<head>
<title> Hello world </title>
</head>
<body>
<!-- Script includes go here -->
{% for file_name in includes %}
<script src="/static/{{ file_name }}" type="text/javascript"></script>
{% end %}
<script type="text/javascript">
// Actual script snippets go here.
{% for script in scripts %}
{% raw script %}
{% end %}
</script>
</body>
embedded_javascript and related methods are (effectively) class-level methods; they must return the same value for any instance of the class. (They're intended to be a kind of dependency-management system, so you can load a piece of javascript only on pages that include a module that needs it)
The only thing that is allowed to vary per instance is the output of render(), so to embed multiple pieces of javascript you should include the script tag in the result of your render() method.

Uncaught SyntaxError: Unexpected token & while rendering a Django template

I'm trying to draw a line chart using "https://www.google.com/jsapi", and passing data from a Django view;
this is my template
<head>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
function drawChart() {
data = {{analytics_data}}
var data = google.visualization.arrayToDataTable(data);
var options = {
title: 'Facebook Analytics'
};
var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
</script>
</head>
<body>
<div id="chart_div" style="width: 900px; height: 500px;"></div>
</body>
</html>
views.py
def show_fb_analytics(request):
analytics_data = [["Day", "Likes", "Share", "Comments"]]
data = FbAnalytics.objects.all()
for item in data:
date = item.date
likes = item.likes
comments = item.comments
shares = item.shares
lst = [date, likes, comments, shares]
analytics_data.append(lst)
return render(request, 'fbchart.html', {'analytics_data':analytics_data})
The analytics_data should return data in format
[['Day', 'Likes', 'Share', 'Comments'],
['31 Aug', 5, 8, 10 ],
['01 Sep', 10, 5, 13 ]]
but during render of the html template it gives data it given format
[['Day', 'Likes', 'Share', 'Comments'],
[u'01Sep', 2, 2, 2]]
means it is adding u'&#39 in every string due to which I'm getting the error "Uncaught Syntax Error: Unexpected token &" and my temlate is not returning the line chart.
How I can remove this error?
You should convert your list to proper JSON first, like this:
import json
def show_fb_analytics(request):
...
return render(request, 'fbchart.html', {'analytics_data': json.dumps(analytics_data)})
Then output it with "safe" filter, so Django's escaping engine doesn't intervene:
{{analytics_data|safe}}
Converting to JSON will output your list as JavaScript Array literal (instead of Python List literal; although the two are pretty similar, they are in fact different, in your case Python has u prefixes which JS doesn't, etc.), and using safe filter will prevent Django's template engine from converting ' to '
#Spc_555's answer is correct but you can mark the JSON as safe in the view too:
import json
from django.utils.safestring import marksafe
def show_fb_analytics(request):
...
return render(request, 'fbchart.html', {'analytics_data': mark_safe(json.dumps(analytics_data))})

Categories

Resources