0

I am currently trying to create a very simple app that displays a recipe and a network diagram.

I have gotten most of the way there, but I am receiving the following error: Uncaught ReferenceError: Bokeh is not defined at HTMLDocument.fn (?description=blueberry+pancakes:29:5)

I have tried implementing the solutions at the following locations:
Python Flask App with Interactive Bokeh plots
Embedding a bokeh plot in Flask
Embedding bokeh plot and datatable in flask
Bokeh plot not visible in flask application

but nothing is really changing.

Below is my current version of index.html.

<html>
<head>
    <title>Recipes</title>
    <style>
        .row {
            display: flex;
        }

        .column {
            flex: 50%;
        }
    </style>
    <script scr="https://cdn.pydata.org/bokeh/release/bokeh-3.0.0.min.js"></script>
    <script scr="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.0.0.min.js"></script>
    <script scr="https://cdn.pydata.org/bokeh/release/bokeh-tables-3.0.0.min.js"></script>
    <script scr="https://cdn.pydata.org/bokeh/release/bokeh-gl-3.0.0.min.js"></script>
    <link
        href="https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css"
        rel="stylesheet" type="text/css">
    <link
        href="https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css"
        rel="stylesheet" type="text/css">
    <link
        href="https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css"
        rel="stylesheet" type="text/css">
    {{script | safe}}
</head>
<!-- <header>
    {{ script|safe }}
</header> -->
<body>

<form action="" method="get">
    Recipe Description: <input type="text" name="description">
    <label for="diets">Choose a Diet:</label>
    <select id="diets" name="diet_plan" size="2" multiple>
        <option value="Vegan">Vegan</option>
        <option value="Vegetarian">Vegetarian</option>
        <option value="Nut-Free">Nut-Free</option>
        <option value="Gluten-Free">Gluten-Free</option>
    </select><br><br>
    <input type="submit" value="Generate a recipe">
</form>

<h1>{{description}}</h1>
<h2>{{diet_plan}}</h2>
<div class="row">
    <div class="column">
        <h2>Ingredients</h2>
        <ul>
            {%for i in range(0, len)%}
                <li>{{ingredients[i]}}</li>
            {%endfor%}
        </ul>
    </div>
    <div class="column">
        <h2>Instructions</h2>
        <ul>
            {%for i in range(0, len)%}
                <li>{{instructions[i]}}</li>
            {%endfor%}
        </ul> 
    </div>
</div>

<h1>{{div}}</h1>

<div>
{{div|safe }}
</div>
  
</body>
</html>

And here is the main portion of main.py.

from flask import Flask
from flask import request, render_template, send_file
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
from bokeh.io import output_file, show
from bokeh.models import (BoxZoomTool, Circle, HoverTool, MultiLine, Plot, Range1d, ResetTool)
from bokeh.palettes import Spectral4
from bokeh.plotting import from_networkx, save
from bokeh.embed import components
from bokeh.resources import CDN
from io import BytesIO
import re

app = Flask(__name__)

@app.route("/")
def index():
    description = request.args.get("description", "")
    # diet_plan = request.form.getlist("diets", "")
    diet_plan = request.args.get("diet_plan", "")
    if description:
        recipe, steps = get_ai_recipe()
        ingredients = recipe["ingredient"] + " (" + recipe["qty"] + ")"
        instructions = steps["instruction"]
        script, div = generate_graph(recipe)
    else:
        recipe = None
        steps = None
        ingredients = pd.DataFrame({"ingredient" : []})
        instructions = pd.DataFrame({"instruction" : []})
        script, div = ("", "")
    return render_template("index.html", 
                            len = len(ingredients), 
                            ingredients = ingredients, 
                            instructions = instructions,
                            description = description,
                            diet_plan = diet_plan,
                            script = script,
                            div = div,
                            resources = CDN.render())

I didn't include the get_ai_recipe() or generate_graph() functions to save some space, but basically get_ai_recipe() returns 2 pandas dataframes, and generate_graph() returns a script and a div from components.

script, div = components(plot)
return script, div

So the "plot" in that code is an actual "Plot" class from bokeh.models. Pretty much every online example I saw when trying to debug this uses figure() instead of Plot(). I'm not sure if that has anything to do with my issue, but if it does, I would appreciate help in figuring out how to convert the Plot to a figure.

Additionally, I am very new to flask and html (this is my very first interaction with them, really), so I'm not sure what all of the scripts/links in the head section are doing. I also do not know if those are the most recent versions of those links. I used the original version from the examples online and then updated them as far as I could, but I'm not sure if they go any further. Some examples used all of those, and some used just one. I assume this is where my issue lies, but I am not sure.

The code is definitely generating the div, as I have it printed as a title on the page, but it's unable to find bokeh.

I would appreciate any help. Thanks!

2 Answers 2

0

You are already passing the div with the plot, but cannot tell how, try using bokeh.plotting.Figure and bokeh.embed.components;

import bokeh
# ...
empty_boxplot = bokeh.plotting.Figure(
            plot_width=500,
            plot_height=450
        )
script, div = bokeh.embed.components(empty_boxplot)

you can then render it with Jinja in your index.html

<div id="plot">
   {{ div }}
</div>
Sign up to request clarification or add additional context in comments.

1 Comment

I tried your suggestion . . . I don't think I understand. bokeh.plotting.Figure is not a thing, at least not in bokeh version 3.0.0. bokeh.plotting.figure is a thing, and it uses attributes width and height (not plot_width and plot_height). I tried adding those in, and i get the same error "bokeh is not defined". Do you have any other ideas?
0

OK I figured it out.

The solution comes from here
Bokeh plot not visible in flask application
Which is a solution I tried to implement but failed to do so correctly. Below is the correct solution.

main.py:

from flask import Flask
from flask import request, render_template, send_file
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
from bokeh.io import output_file, show
from bokeh.models import (BoxZoomTool, Circle, HoverTool, MultiLine, Plot, Range1d, ResetTool)
from bokeh.palettes import Spectral4
from bokeh.plotting import from_networkx, save, figure
from bokeh.embed import components
from bokeh.resources import CDN
from io import BytesIO
import re

app = Flask(__name__)

@app.route("/")
def index():
    description = request.args.get("description", "")
    # diet_plan = request.form.getlist("diets", "")
    diet_plan = request.args.get("diet_plan", "")
    if description:
        recipe, steps = get_ai_recipe()
        ingredients = recipe["ingredient"] + " (" + recipe["qty"] + ")"
        instructions = steps["instruction"]
        script, div = generate_graph(recipe)
    else:
        recipe = None
        steps = None
        ingredients = pd.DataFrame({"ingredient" : []})
        instructions = pd.DataFrame({"instruction" : []})
        script, div = ("", "")
    return render_template("index.html", 
                            len = len(ingredients), 
                            ingredients = ingredients, 
                            instructions = instructions,
                            description = description,
                            diet_plan = diet_plan,
                            script = script,
                            div = div,
                            resources = CDN.render())

index.html:

<html>
<head>
    <title>Recipes</title>
    <style>
        .row {
            display: flex;
        }

        .column {
            flex: 50%;
        }
    </style>
    {{ resources|safe }}
</head>
<header>
    {{ script|safe }}
</header>
<body>

<form action="" method="get">
    Recipe Description: <input type="text" name="description">
    <label for="diets">Choose a Diet:</label>
    <select id="diets" name="diet_plan" size="2" multiple>
        <option value="Vegan">Vegan</option>
        <option value="Vegetarian">Vegetarian</option>
        <option value="Nut-Free">Nut-Free</option>
        <option value="Gluten-Free">Gluten-Free</option>
    </select><br><br>
    <input type="submit" value="Generate a recipe">
</form>

<h1>{{description}}</h1>
<h2>{{diet_plan}}</h2>
<div class="row">
    <div class="column">
        <h2>Ingredients</h2>
        <ul>
            {%for i in range(0, len)%}
                <li>{{ingredients[i]}}</li>
            {%endfor%}
        </ul>
    </div>
    <div class="column">
        <h2>Instructions</h2>
        <ul>
            {%for i in range(0, len)%}
                <li>{{instructions[i]}}</li>
            {%endfor%}
        </ul> 
    </div>
</div>

<h1>{{div}}</h1>

<div>{{div|safe }}</div>
  
</body>
</html>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.