How do I add a pdfPages that I created in python to my flask interface?

66 views Asked by At

I have created a python program that takes a few inputs and then creates several plots and saves them in one big pdf using pdfPages. Now I want to create a local web interface using flask which opens this pdf to make it easily viewable. I do not have a lot of Flask experience so I don't know what the nicest way to do this is? Also, using request.data doesn't seem a good option to me, since I have a pdf I would like to open and as I understand it request is used for data like jsons or strings and not entire pdfs.

My current idea would be to call the function and let it create and save the plots and then open the pdf in Flask. However, this doesn't work for me. Is there a better way to do it? If not, can you recommend tips how I can make this run?

My current code looks something like this:

import os
from flask import Flask, send_file

app = Flask(__name__)

@app.route('/')
def welcome():

    param1 = str(input('Enter the first parameter: '))
    param2 = str(input('Enter the second parameter: '))
    plotting_function(param1=param1, param2=param2)

    file_path=os.path.dirname(__file__) + '/plots' + "/plots_for_param_" + param1 + ".pdf" 
# This is the name of the file and the path where it is being stored

    with open(file_path, 'rb') as static_file:
        return send_file(static_file)


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

However, if I run this code I get an error: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSWindow drag regions should only be invalidated on the Main Thread!'

After the responses from @Detlef, I have improved my code so it looks like this:

@app.route('/')
def welcome():
    param1 = str(input('Enter the first parameter: '))
    param2 = str(input('Enter the second parameter: '))
    plotting_function(param1=param1, param2=param2)

#The name is the name of the file that is being created by the plotting function 
   name = str("plots_for_user_" + param1 + ".pdf")
   return download_file(name,param1)

@app.route('/user/<name>')
def download_file(name,param1):
# /plots/plots_ specifies the path of the folder that the created file that is to be loaded is saved into, starting from the current path where the flask application is running
# name is the name of the file
    return flask.send_from_directory(
        '/plots/plots_' + param1 + '_/', name, as_attachment=True
    )
if __name__ == '__main__':
    app.run(debug=True)

However, with this I run into the issue of th Url not being found and I have no idea where to start, since the error message is so unspecific. Any help greatly appreciated!! Thank you for your help!

1

There are 1 answers

5
Detlef On

From what I know so far, two inputs from the user are required to create a PDF.

In my very simplified example, a form is displayed which allows the user to make entries. The form is sent to the server via POST request, where the entries are queried based on the defined attributes for the names of the input fields. Now the function plotting_function(param, path), which I don't know, is called, which probably creates the PDF. Based on the path information provided in your example, the generated PDF should then be downloaded automatically.

from flask import (
    Flask, 
    request, 
    render_template_string, 
    send_from_directory
)
from matplotlib.figure import Figure
from matplotlib.backends.backend_pdf import PdfPages
import datetime
import os 
import tempfile

app = Flask(__name__)
app.config.from_mapping(PLOTS_FOLDER=os.path.join(app.instance_path, 'plots'))
os.makedirs(app.config['PLOTS_FOLDER'], exist_ok=True)

def plotting_function(path, name, param2):
    # Create the PDF file.
    with PdfPages(path) as pdf:

        # Generate sample data.
        for i in range(1,5):
            fig = Figure()
            ax = fig.subplots()
            ax.plot([1, i])
            pdf.savefig(fig)

        # Set metadata for the PDF file.
        d = pdf.infodict()
        d['Title'] = f'{name.capitalize()}s PDF Example'
        d['Author'] = 'Rosarosa'
        d['Subject'] = 'How to create a multipage pdf file and set its metadata'
        d['Keywords'] = 'PdfPages multipage keywords author title subject'
        d['CreationDate'] = datetime.datetime.today()
        d['ModDate'] = datetime.datetime.today()


@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        # When the form is sent, user input can be 
        # requested based on the name.
        param1 = request.form.get('param1')
        param2 = request.form.get('param2')
        if param1 and param2: 
            # If the input is valid, a temporary directory is created 
            # and the function is called.
            
            plotsdir = app.config['PLOTS_FOLDER']
            with tempfile.TemporaryDirectory(dir=plotsdir) as tmpdir:
                file = f'plots_for_user_{param1.lower()}.pdf'
                path = os.path.join(tmpdir, file)

                plotting_function(path, param1, param2)

                # The file will be downloaded.
                return send_from_directory(
                    tmpdir, 
                    file, 
                    as_attachment=True
                )

    # When the page is accessed, a form is displayed for user input.
    return render_template_string('''
        <form method="post">
            <div>
                <label for="param1">Param 1</label>
                <input id="param1" name="param1" />
            </div>
            <div>
                <label for="param2">Param 2</label>
                <input id="param2" name="param2" />
            </div>
            <button type="submit">Submit</button>
        </form>
    ''')

Keep in mind that, as already mentioned, this example is very minimal and should be modified and expanded based on your requirements.