Generate pdf report from dataframe and charts using Jinja2 and pdfkit in streamlit

I developed an app that reads csv files and then provides the user with a summary of data in the form of a dataframe and a chart. I would like to provide the user with the option of downloading these results in a pdf format. I found these code examples to be helpful: Creating PDF Reports with Python, Pdfkit, and Jinja2 Templates | by Mark Nagelberg | Towards Data Science GitHub - lcalmbach/report-generator-for-long-tables: project allows to generatate pdf tables with long tables, having repeating headers

However, when I tried to follow these guides, I ended up with the following error " TemplateNotFound"

Full error message:

TemplateNotFound: reportTemplate.html
Traceback:
File "C:\Users\farha\anaconda3\envs\streamlitenv\lib\site-packages\streamlit\runtime\scriptrunner\script_runner.py", line 565, in _run_script
    exec(code, module.__dict__)
File "C:\Users\farha\Desktop\GADoha\pages\5_Flow Accumulation Report.py", line 116, in <module>
    template = templateEnv.get_template(TEMPLATE_FILE)
File "C:\Users\farha\anaconda3\envs\streamlitenv\lib\site-packages\jinja2\environment.py", line 1010, in get_template
    return self._load_template(name, globals)
File "C:\Users\farha\anaconda3\envs\streamlitenv\lib\site-packages\jinja2\environment.py", line 969, in _load_template
    template = self.loader.load(self, name, self.make_globals(globals))
File "C:\Users\farha\anaconda3\envs\streamlitenv\lib\site-packages\jinja2\loaders.py", line 126, in load
    source, filename, uptodate = self.get_source(environment, name)
File "C:\Users\farha\anaconda3\envs\streamlitenv\lib\site-packages\jinja2\loaders.py", line 218, in get_source
    raise TemplateNotFound(template)

A minimum Python code example:

import streamlit as st
import pandas as pd
import numpy as np
import altair as alt
import numpy as np
import pdfkit
import jinja2

uploaded_file = st.file_uploader("", accept_multiple_files=False) 
df= pd.read_csv(uploaded_file[0])

TEMPLATE_FILE = "reportTemplate.html"

templateLoader = jinja2.FileSystemLoader(searchpath="./")
templateEnv = jinja2.Environment(loader=templateLoader)
template = templateEnv.get_template(TEMPLATE_FILE)

outputText = template.render(df=df, month='May')
html_file = open('report.html', 'w')
html_file.write(outputText)
html_file.close()
 

HTML template

<!DOCTYPE html>
<head></head>
<body>
    <h1>Flow Accumulation Report: {{ month }}%</h1> 
    <table>
        <tr>
            {% for column in df.columns %}
            <th>{{ column }}</th>
            {% endfor %}
        </tr>
        {% for idx, row in df.iterrows() %}
        <tr>
            {% for colname in df.columns %}
            <td>${{ row[colname] | round(1) }}</td>
            {% endfor %}
        </tr>
        {% endfor %}
    </table>    
</body>

Folder Structure

App folder contains:

-1- app.py
-2- pages folder contains several streamlit pages (e.g., page1.py, page2.py, etc.)
-3- reportTemplate.html

Screenshot of the app folder
folderStructure

Hi @farhat

Thanks for the question and for sharing the code and app details. Could you also share the entirety of the error message displayed in the error log (of deployed apps by clicking on the Streamlit icon at the lower right hand corner to bring up the error log).

Best regards,
Chanin

It is probably a simple path issue or a typo.
But we have to see the whole file structure of your repository, otherwise we can only guess…

Thank you for your reply. I have edited my question to include the full error message

Thank you for your reply. I have added a screenshot of the folder app in my question.
As a note, when looking for a solution, I read that flask requires the template file to be in a separate templates folder >> I tried that but it did not work out for me.

After manually checking the directory assigned to the template file, I understood the problem. My mistake was that I was asking the code to get the “template.html” file from the pages folder.

I think I better understand now why Flask imposes that the template is always called from its own separate folder :sweat_smile:. It actually leads to fewer mistakes.

I modified my streamlit code in the following way:

pages_path = os.path.dirname(__file__)
app_path = os.path.dirname(pages_path)
                    
path=os.path.join(app_path,'./templates')
templateLoader = jinja2.FileSystemLoader(searchpath=path)
templateEnv = jinja2.Environment(loader=templateLoader)
TEMPLATE_FILE = "template.html"
template = templateEnv.get_template( TEMPLATE_FILE )

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.