1

I have created a simple Flask app which renders a template 'index.html' and in that HTML I am attempting to list various plots as a sort of dashboard-style webpage with other content. I know the basics of Flask and Dash though am not using Dash as I want to have more control over the HTML/CSS hence using Flask to create a website to embed the graphs using Plotly.

So far I've had no luck with any of the official documentation or any medium.com or suchlike articles. The closest I have come to is this answer: Embedding dash plotly graphs into html

However, it isn't working when I run my app and the browser launches in localhost. Instead it just gives me a lot of text which is clearly the plotly figure, but it isn't turning into a graph.

Here is all my py/html/css even if the navbar stuff isn't relevant; just in case (I am still learning so I'm sure there will be some better ways to do things..)

Thanks for any help.

DataFrame class which grabs the latest Coronavirus data and returns as pandas.dataframe:

import pandas as pd
import requests


    class DataFrame:
        """
        Class which grabs the data live from the ECDC and returns it in a pandas dataframe
        """

        def __init__(self):
            """
            Creating the pandas dataframe of the ECDC JSON data
            """
            self.url = "https://opendata.ecdc.europa.eu/covid19/casedistribution/json"
            self.file = requests.get(self.url).json()
            self.file = self.file['records']
            self.df = pd.DataFrame(data=self.file)

        def converter(self):
            """
            Converting the dtypes from object to int for ints, and date to date
            Also renames the columns to more visual-friendly names
            :return: None
            """
            self.df['cases'] = self.df['cases'].astype(int)
            self.df['deaths'] = self.df['deaths'].astype(int)
            self.df['popData2018'] = self.df['popData2018'].astype(str).replace('', 0).astype(int)
            self.df['dateRep'] = self.df['dateRep'].to_timestamp
            cols_rename = 'date day month year cases deaths country geo_id country_id population continent'.split()
            cols_rename = [s.capitalize() for s in cols_rename]
            self.df.columns = cols_rename

        def return_df(self):
            """
            :return: pandas DataFrame
            """
            self.converter()
            return self.df

app.py

from plotly.offline import plot
import plotly.graph_objects as go
from dataframe.dataframe import DataFrame
from flask import Flask, render_template, redirect, request, url_for

app = Flask(__name__)


def graph_maker():
    df = DataFrame().return_df()
    data = []

    for continent in df['Continent'].unique():
        df_filt = df[df['Continent'] == continent]
        data.append(go.Scatter(x=df_filt["Cases"],
                               y=df_filt["Deaths"],
                               mode='markers',
                               text=df_filt['Country'],
                               name=continent))

    layout = go.Layout(title="Deaths (Y) v Cases (X) by continent")

    fig = go.Figure(data=data, layout=layout)

    return plot(figure_or_data=fig,
                include_plotlyjs=False,
                output_type='div')


@app.route('/')
def index():
    graph = graph_maker()
    return render_template('index.html',
                           graph=graph)


if __name__ == '__main__':
    app.run(debug=True)

index.html

{% extends "navbar.html" %}
<head>
    <meta charset="UTF-8">
    <link type="text/css" rel="stylesheet" href="..\static\master.css">
    <link href="https://fonts.googleapis.com/css2?family=Maven+Pro&display=swap" rel="stylesheet">
    <!-- Plotly.js -->
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>

</head>
{% block nbar %}
<body>
<div class="global-box" id="global-stats">
    <h1>Global charts</h1>
    <p>Title here</p>
    <ul class="global-box-ul">
        <li class="global-box-ul-li">
            {{ graph }}
        </li>
        <li class="global-box-ul-li">
            Another chart here
        </li>
    </ul>
</div>
</body>
{% endblock %}

navbar.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>C19DB</title>
    <meta charset="UTF-8">
    <link type="text/css" rel="stylesheet" href="..\static\master.css">
    <link href="https://fonts.googleapis.com/css2?family=Maven+Pro&display=swap" rel="stylesheet">
</head>

<nav class="navbar">
    <div class="logo">c19db</div>
    <div class="list">
    <ul class="navbar_items">
       <li class="navbar_item"><a href="#">Dashboard</a></li>
       <li class="navbar_item"><a href="#">About</a></li>
       <li class="navbar_item"><a href="#">Register</a></li>
    </ul>
    </div>
</nav>

{% block nbar %}

{% endblock %}
</html>

master.css

html, body {
    font-family: 'Maven Pro';
    height: 700px;
    margin: 0;
}

.navbar {
    background: rgb(237, 232, 232);
    vertical-align: middle;
}

.logo {
    vertical-align: middle;
    display: inline-block;
    color: rgb(196, 69, 69);
    font-size: 50px;
    width: 250px;
    padding: 5px 15px 5px 15px;
}

.list{
    vertical-align: middle;
    display: inline-block;
    width: calc(100% - 285px);
    text-align: right;
}

.navbar_items {
    list-style: none;
    font-size: 20px;
    color: rgb(61, 61, 61)
}

.navbar_item{
    display: inline-block;
    padding: 5px 15px 5px 15px;
}

a {
    text-decoration: none;
}

.navbar_item > a{
    display: inline-block;
    padding: 5px 15px 5px 15px;
    color: rgb(61, 61, 61);
}

.navbar_item > a:hover {
    display: inline-block;
    padding: 5px 15px 5px 15px;
    color: rgb(196, 69, 69);
}

.footer, .footer a {
    position: relative;
    background: rgb(237, 232, 232, 0.2);
    width: 100%;
    color: rgb(61, 61, 61, 0.2);
    text-align: center;
}

span {
    font-weight: bold;
}

.global-box {
    text-align: center;
    border: 2px black solid;
    list-style: none;
    margin: auto;
}

.global-box > h1, .global-box > p {
    margin: 1px;
}

ul {
    display: contents;
}

.global-box-ul-li {
    display: inline-block;
    border: 2px lightblue solid;
    list-style: none;
    margin: auto;
    width: 48%;
    height: 100%;
}

Thank you for any help!

Paul Wilson
  • 562
  • 5
  • 16

1 Answers1

1

I have solved this problem.

Nutshell:

  1. Create a chart
  2. Call pyo.plot() as normal passing through the fig, output_type='div' and include_plotlyjs=False
  3. Have that output to a variable passed through Markup() (import from flask)
  4. Have the Markup(variable) passed through the render_template like you would a form
  5. Have the variable rendered in the html using {{ jinja template }}

First, create your Plotly chart like normal. I will not give a whole example but just the key points. I create charts in functions for import and use in multiple pages if necessary. In this case, it's necessary because the chart must be assigned to a variable.

def my_bar_chart():
    *snip irrelevant*
    my_bar_chart = pyo.plot(fig, output_type='div', include_plotlyjs=False)
    return Markup(my_bar_chart)

Now import your function to your app.py / wherever your views are and pass it through render template as you would any form, for example.

Here is an example:

def my_page():
    my_bar_chart_var = my_bar_chart()
    return render_template('my_page.html',
                            bar_chart_1=my_bar_chart_var)

Then on the html for that page simply pass through bar_chart_1 in a jinja template like so:

{{ bar_chart_1 }}

And done.

Paul Wilson
  • 562
  • 5
  • 16