Home > Software design >  Python flask multiprocess: cannot "render" a html page for the send grid email body
Python flask multiprocess: cannot "render" a html page for the send grid email body

Time:12-10

Oke to bring some context I have big specific function that is running in the background as a multiprocess. Aka a second process (possibly more later on).

The reason for this is that it will redirect to a html page saying the request was successful. After all a client will not wait over potentially 7 minutes on a loading page before his data request came through and the data is converted. Yes this is essential what my application does. And before you ask why this takes so long has to do with how the JSON data is structured.

AKA in short doing a fist request to get the list of data points, and doing a second request for each datapoint seperatly, because the request url for the details is inside that datapoint.

In the final step of this background process, a mail will be send by send grid and depending on if the file is too big or not it will send an attachment with a special "local file" link to download it. The text of the mail will have different content depending on the situation, but the main thing you have to take away from this, is whether or not the file as an attachment or not, you will always have the local file "download location".

import os
import base64
from datetime import datetime
from flask import Flask
import  Classified.background_script.converter.GIPOD_Converter as data_conv
from flask import send_file, send_from_directory, safe_join, abort, render_template, jsonify
from Classified import app
from celery import Celery
import random
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import (
    Mail, Attachment, FileContent, FileName,
    FileType, Disposition, ContentId)
from sendgrid import SendGridAPIClient
import Classified.background_script.sendgrid as mail_delivery


send_grid_mail = 'Classified'
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))

app = Flask(__name__)


def main(file_name: str,file_location:str, attachment_bool: bool, download_link:str, recipient_email: str):
    file_path = file_location
    if os.getenv('SENDGRID_API_KEY') == None:
        print("No API KEY FOUND")
    else:
        print("API KEY FOUND")
    html_string = generate_html_content(attachment_bool,download_link)
    message = Mail(
            from_email='Classified',
            to_emails=recipient_email,
            subject='Uw data is gereed.',
            html_content= html_string)

    if attachment_bool is True:
        with open(file_path, 'rb') as f:
            data = f.read()
            f.close()
        encoded_file = base64.b64encode(data).decode()
        attachment = Attachment()
        attachment.file_content = FileContent(encoded_file)
        attachment.file_type = FileType('application/zip')
        attachment.file_name = FileName('{}.zip'.format(file_name))
        attachment.disposition = Disposition('attachment')
        attachment.content_id = ContentId('Example Content ID')
        message.attachment = attachment
    try:     
            response = sg.send(message)
            print(response.status_code)
            print(response.body)
            print(response.headers)
    except Exception as e:
            print("Failed scenario")
            print("Scenerio checking")


# Generates the HTML variables for the email page.
def generate_html_content(attachment_bool: bool, download_link: str):
    if attachment_bool is False:
        Letter_1 = "Helaas was deze te groot om via mail door te sturen."
        Letter_2 = "Klik hier om het bestand te downloaden."
    if attachment_bool is True:
        Letter_1 =  "U vindt het bestand terug in de bijlage."
        Letter_2 = "Is het bestand niet in de bijlage terug te vinden? Klik dan hier en download dan hier."
    return render_template(
        'email.html',
        message_1 = Letter_1,
        message_2 = Letter_2,
        link = download_link,
        title='YOU GOT MAIL'
         )










if __name__ == "__main__":
    main()

You maybe asking why I use a html page render function? Well its because of the follwoing

In essential I am kind of rendering a static template with variables

But I encountered a specific problem I did not ecounter when it was not a multiprocess.

Traceback (most recent call last):
  File "D:\IDE\Anaconda3\envs\PYTHONGDAL\lib\multiprocessing\process.py", line 315, in _bootstrap
    self.run()
  File "D:\IDE\Anaconda3\envs\PYTHONGDAL\lib\multiprocessing\process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "D:\Programmeer portfolio\FLASK\Classfiied\Classfiied\views.py", line 178, in execute_order66
    Order66.main(stringpart,url,sper_year,email)
  File "D:\Programmeer portfolio\FLASK\Classfiied\Classfiied\background_script\backgroundtask.py", line 53, in main
    mail_delivery.main(file_name,requested_data,attachment_bool,requested_data,email)
  File "D:\Programmeer portfolio\FLASK\Classfiied\Classfiied\background_script\sendgrid\send_mail.py", line 30, in main
    html_string = generate_html_content(attachment_bool,download_link)
  File "D:\Programmeer portfolio\FLASK\Classfiied\Classfiied\background_script\sendgrid\send_mail.py", line 67, in generate_html_content
    return render_template(
  File "D:\IDE\Anaconda3\envs\Classfiied\lib\site-packages\flask\templating.py", line 146, in render_template
    ctx.app.update_template_context(context)
AttributeError: 'NoneType' object has no attribute 'app'

I do not get why I get this error is there something special I need to do or do I have to go a different route, and write the full HTML anyway?

CodePudding user response:

Twilio SendGrid developer evangelist here.

The error, at the end of the trace there, is:

  File "D:\IDE\Anaconda3\envs\Classfiied\lib\site-packages\flask\templating.py", line 146, in render_template
    ctx.app.update_template_context(context)
AttributeError: 'NoneType' object has no attribute 'app'

Which tells me that the ctx object is NoneType here, and it shouldn't be. I think the issue is that you are running this code outside of the context of a flask application, it's not running a Flask route in response to an incoming HTTP request, you're just calling it as a regular function. When it wasn't multiprocess, I'm guessing this ran in the context of a Flask route.

I had a look around at how you might use render_template outside of a Flask route and eventually came across these examples on Full Stack Python, particularly example 4, which used render_template to render HTML that is then sent as an email. That example is from flask-base and uses with app.app_context() to run code within the context of a Flask application without it being part of a route.

I'm not a Python programmer normally, but maybe something like this will work for your app:

def generate_html_content(attachment_bool: bool, download_link: str):
    if attachment_bool is False:
        Letter_1 = "Helaas was deze te groot om via mail door te sturen."
        Letter_2 = "Klik hier om het bestand te downloaden."
    if attachment_bool is True:
        Letter_1 =  "U vindt het bestand terug in de bijlage."
        Letter_2 = "Is het bestand niet in de bijlage terug te vinden? Klik dan hier en download dan hier."
    with app.app_context():
      return render_template(
          'email.html',
          message_1 = Letter_1,
          message_2 = Letter_2,
          link = download_link,
          title='YOU GOT MAIL'
      )

Let me know if that helps at all.

  • Related