Yuri
Yuri

Reputation: 1

Flask "error": "415 Unsupported Media Type: Did not attempt to load JSON data because the request Content-Type was not 'application/json'."

I am building a backend for my react app using flask. Using an openai api to generate cover letters from submitted form data.

from flask import Flask, request, jsonify
from flask_cors import CORS
import os
from dotenv import load_dotenv
from .body_constructor import BodyConstructor
from .coverletter_assembler import CoverletterAssembler
from .head_constructor import HeadConstructor

load_dotenv()

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "http://localhost:3000"}})

if not os.getenv('OPENAI_API_KEY'):
    raise ValueError("No OpenAI API key found. Set the OPENAI_API_KEY environment variable.")

@app.route('/generate_cover_letter', methods=['GET', 'POST'])
def generate_cover_letter():
    try:
        data = request.json
        if not data:
            raise ValueError("No data provided")

        # Log received data for debugging
        print("Received data:", data)
        customer_id = data.get('customer_id')
        doc_id = data.get('doc_id')
        surname = data.get('surname')
        first_name = data.get('first_name')
        street = data.get('street')
        postal_code = data.get('postal_code')
        city = data.get('city')
        company_name = data.get('company_name')
        company_street = data.get('company_street')
        company_postal_code = data.get('company_postal_code')
        company_city = data.get('company_city')
        date = data.get('date')
        job_title = data.get('job_title')
        contact_person = data.get('contact_person')
        reason_job_search = data.get('reason_job_search')
        found_on = data.get('found_on')
        work_experience = data.get('work_experience')
        education = data.get('education')
        further_skills = data.get('further_skills')
        tasks = data.get('tasks')
        requirements = data.get('requirements')
        salutation = data.get('salutation')
        business_objective_department = data.get('business_objective_department')
        business_objective_company = data.get('business_objective_company')

        assembler = CoverletterAssembler(
            customer_id, doc_id, surname, first_name, street, postal_code, city,
            company_name, company_street, company_postal_code, company_city, date,
            job_title, contact_person, reason_job_search, found_on, work_experience,
            education, further_skills, tasks, requirements, salutation,
            business_objective_department, business_objective_company
        )

        latex_doc = assembler.create_latex_doc()
        result = assembler.create_latex_pdf(latex_doc)
        cover_letter_string, applicant_address, company_address, date, subject_line = assembler.create_cover_letter_string()
        assembler.create_reportlab_pdf(cover_letter_string, applicant_address, company_address, date, subject_line)

        return jsonify({
            'latex_doc': latex_doc,
            'cover_letter_string': cover_letter_string,
            'result': result.returncode
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500

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

This error is thrown when i run the flask app

"error": "415 Unsupported Media Type: Did not attempt to load JSON data because the request Content-Type was not 'application/json'."

When I send the request POST http://localhost:5000/generate_cover_letter from my frontend, it gives 500 (INTERNAL SERVER ERROR) Error generating cover letter: {error: "cannot access local variable 'result' where it is not associated with a value"}

my front end looks like this:

const CoverLetterForm = () => {
    const [formData, setFormData] = useState({
        customer_id: '',
        doc_id: '',
        surname: '',
        first_name: '',
        street: '',
        postal_code: '',
        city: '',
        company_name: '',
        company_street: '',
        company_postal_code: '',
        company_city: '',
        date: '',
        job_title: '',
        contact_person: '',
        reason_job_search: '',
        found_on: '',
        work_experience: '',
        education: '',
        further_skills: '',
        tasks: '',
        requirements: '',
        salutation: '',
        business_objective_department: '',
        business_objective_company: ''
    });
    const [coverLetter, setCoverLetter] = useState('');
    const [error, setError] = useState('');

    const handleChange = (e) => {
        setFormData({
            ...formData,
            [e.target.name]: e.target.value
        });
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            const response = await axios.post('http://localhost:5000/generate_cover_letter', formData);
            setCoverLetter(response.data);
            setError('');  // Clear any previous errors
        } catch (error) {
            console.error('Error generating cover letter:', error.response ? error.response.data : error.message);
            setError('Failed to generate cover letter. Please check the server logs for more details.');
        }
    };

To clarify, this is my coverletter_assembler code:


import os
import subprocess
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import Paragraph

from .head_constructor import HeadConstructor
from .body_constructor import BodyConstructor


class CoverletterAssembler:
    """
    A class to assemble and generate a job application cover letter in both LaTeX and PDF formats.

    Attributes:
        head_constructor (HeadConstructor): Instance of HeadConstructor to create header content.
        body_constructor (BodyConstructor): Instance of BodyConstructor to create body content.
        full_name (str): Full name of the applicant.
        customer_id (str): Customer ID for identifying the applicant.
        doc_id (str): Document ID for the cover letter.
    """

    def __init__(self, customer_id, doc_id, surname, first_name, street, postal_code, city, company_name, company_street, company_postal_code, company_city, date, job_title, contact_person, reason_job_search, found_on, work_experience, education, further_skills, tasks, requirements, salutation, business_objective_department, business_objective_company):
        """
        Initializes the CoverletterAssembler with applicant and job details.

        Parameters:
            customer_id (str): Customer ID for identifying the applicant.
            doc_id (str): Document ID for the cover letter.
            surname (str): Surname of the applicant.
            first_name (str): First name of the applicant.
            street (str): Street address of the applicant.
            postal_code (str): Postal code of the applicant.
            city (str): City of the applicant.
            company_name (str): Name of the company.
            company_street (str): Street address of the company.
            company_postal_code (str): Postal code of the company.
            company_city (str): City of the company.
            date (str): Date for the document.
            job_title (str): Job title for the application.
            contact_person (str): Contact person at the company.
            reason_job_search (str): Reason for the job search.
            found_on (str): Where the job ad was found.
            work_experience (str): Applicant's work experience.
            education (str): Applicant's educational background.
            further_skills (str): Applicant's further skills.
            tasks (str): Future tasks of the applicant.
            requirements (str): Requirements for the job.
            salutation (str): Salutation for the letter.
            business_objective_department (str): Business objectives of the department.
            business_objective_company (str): Business objectives of the company.
        """
        self.head_constructor = HeadConstructor(surname, first_name, street, postal_code, city, company_name, company_street, company_postal_code, company_city, date, job_title, contact_person)
        date = self.head_constructor.get_formatted_date()
        salutation = self.head_constructor.create_salutation()
        
        self.body_constructor = BodyConstructor(reason_job_search, found_on, work_experience, education, further_skills, date, tasks, requirements, salutation, business_objective_department, business_objective_company, job_title)
        
        self.full_name = self.head_constructor.get_full_name()

        self.customer_id = customer_id

        self.doc_id = doc_id

    def create_latex_doc(self):
        """
        Creates a LaTeX document for the cover letter.

        Returns:
            str: The LaTeX formatted cover letter.
        """
        latex_head = self.head_constructor.create_latex_head()
        latex_body = self.body_constructor.create_latex_body()

        latex_start = r"""
\documentclass[a4paper,12pt]{article}
\usepackage[utf8]{inputenc}
\usepackage{fontenc}
\usepackage[german]{babel}
\usepackage{microtype}

\usepackage{geometry}

\geometry{
a4paper,
margin=25mm,
left=25mm,
right=25mm,
top=20mm,
bottom=30mm
}

\begin{document}
"""

        latex_end = rf"""
\noindent Mit freundlichen Grüßen \\ \newline
{self.full_name}
\end{{document}}"""

        latex_doc = latex_start + latex_head + latex_body + latex_end

        return latex_doc
    
    def create_latex_pdf(self, latex_doc):
        """
        Compiles the LaTeX document to a PDF file.

        Parameters:
            latex_doc (str): The LaTeX formatted cover letter.

        Returns:
            subprocess.CompletedProcess: The result of the LaTeX compilation process.
        """
        tex_filename = f"coverletter_{self.customer_id}_{self.doc_id}.tex"
        output_dir = "constructed_coverletter"

        # Ensure the directory exists
        os.makedirs(output_dir, exist_ok=True)

        tex_file_path = os.path.join(output_dir, tex_filename)
        with open(tex_file_path, 'w', encoding='utf-8') as tex_file:
            tex_file.write(latex_doc)

        # Compile the .tex file to PDF using pdflatex
        try:
            result = subprocess.run(
                ["pdflatex", "-interaction=nonstopmode", tex_file_path],
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )
            # print(f"PDF generated successfully and saved as {tex_filename.replace('.tex', '.pdf')}")
        except FileNotFoundError:
            raise RuntimeError("pdflatex not found. Please ensure it is installed and available in your system's PATH.")
        except subprocess.CalledProcessError as e:
            print("An error occurred while compiling the LaTeX document:")
            print(e.stdout)
            print(e.stderr)

        return result
    
    def create_reportlab_pdf(self, cover_letter_string, applicant_address, company_address, date, subject_line):
        """
        Creates a PDF cover letter using ReportLab.

        Parameters:
            cover_letter_string (str): The full cover letter text.
            applicant_address (str): The applicant's address.
            company_address (str): The company's address.
            date (str): The date for the document.
            subject_line (str): The subject line of the cover letter.

        Returns:
            None
        """
        c = canvas.Canvas(f"constructed_coverletter/coverletter_{self.doc_id}_{self.customer_id}.pdf", pagesize=letter)
        width, height = letter  # Keep these for easy reference

        initial_height = 750
        c.setFont("Helvetica", 12)

        styles = getSampleStyleSheet()
        normal_style = styles['BodyText']
        normal_style.wordWrap = 'CJK'  # This helps with wrapping words
        normal_style.fontName = 'Helvetica'  # Ensure the font matches
        normal_style.fontSize = 12
        
        # Applicant Address - Right-aligned
        for count1, line in enumerate(applicant_address.split('\n')):
            c.drawRightString(550, initial_height-(15*count1), line)
        
        # Company Address - Left-aligned
        height_post_address = initial_height-(15*count1)
        for count2, line in enumerate(company_address.split('\n')):
            c.drawString(50, height_post_address-(15*count2), line)

        # Date - Right-aligned
        height_post_address = height_post_address-(15*count2)
        c.drawRightString(550, height_post_address-20, date.replace("\n", ""))

        # Subject Line - Bold
        c.setFont("Helvetica-Bold", 12)
        c.drawString(50, height_post_address-20*3, subject_line.replace("\n", ""))

        # Salutation
        salutation = cover_letter_string.split('\n\n')[0]
        c.setFont("Helvetica", 12)
        c.drawString(50, height_post_address-20*5, salutation.replace("\n", ""))

        # Paragraph list - assume you have multiple paragraphs to add
        first_paragraph = cover_letter_string.split('\n\n')[1]
        second_paragraph = cover_letter_string.split('\n\n')[2]
        third_paragraph = cover_letter_string.split('\n\n')[3]
        fourth_paragraph = cover_letter_string.split('\n\n')[4]
        final_paragraph = cover_letter_string.split('\n\n')[5]
        final_sentence = cover_letter_string.split('\n\n')[6]
        paragraphs = [first_paragraph, second_paragraph, third_paragraph, fourth_paragraph, final_paragraph, final_sentence]  # Add more as needed
        current_y_position = height_post_address-20*5

        for para in paragraphs:
            p = Paragraph(para, normal_style)
            effective_width = width - 100  # Assuming a margin of 50 on each side
            w, h = p.wrap(effective_width, height)  # Find required space
            current_y_position -= (h + 10)  # Move up the cursor by the height of the paragraph and margin
            p.drawOn(c, 50, current_y_position)  # Draw the paragraph at the adjusted position

        # Closing
        height_post_paragraphs = current_y_position
        c.drawString(50, height_post_paragraphs-30, "Mit freundlichen Grüßen")

        # Name
        height_post_closing = height_post_paragraphs-30
        c.drawString(50, height_post_closing-30, f"{self.head_constructor.get_full_name()}")


        c.showPage()
        c.save()

    def create_cover_letter_string(self):
        """
        Creates the full cover letter string by combining all parts.

        Returns:
            tuple: A tuple containing the cover letter string, applicant address, company address, date, and subject line.
        """
        applicant_address, company_address, date, subject_line, salutation = self.head_constructor.get_header_parts()
        first_paragraph, second_paragraph, remaining_main_paragraphs, final_paragraph, final_sentence = self.body_constructor.get_body_parts()

        applicant_name = applicant_address.split('\n')[0]
        cover_letter_string = salutation + "\n" + first_paragraph + "\n\n" + second_paragraph + "\n\n"  + remaining_main_paragraphs.replace("\\\\", "") + "\n\n"  + final_paragraph + "\n\n"  + final_sentence + "\n\nMit freundlichen Grüßen \n\n" + applicant_name
        
        return cover_letter_string, applicant_address, company_address, date, subject_line

    def cover_letter_trigger(self):
        """
        Generates the cover letter PDF and creates the cover letter string.

        Returns:
            str: The full cover letter string.
        """
        applicant_address, company_address, date, subject_line, salutation = self.head_constructor.get_header_parts()
        first_paragraph, second_paragraph, remaining_main_paragraphs, final_paragraph, final_sentence = self.body_constructor.get_body_parts()

        cover_letter_string = self.create_cover_letter_string()
        self.create_reportlab_pdf(cover_letter_string, applicant_address, company_address, date, subject_line)

        return cover_letter_string

Upvotes: -1

Views: 120

Answers (1)

John Gordon
John Gordon

Reputation: 33351

The problem is here, in CoverletterAssembler.create_latex_pdf():

try:
    result = subprocess.run(...)
except FileNotFoundError:
    raise RuntimeError("pdflatex not found. Please ensure it is installed and available in your system's PATH.")
except subprocess.CalledProcessError as e:
    print("An error occurred while compiling the LaTeX document:")
    print(e.stdout)
    print(e.stderr)

return result

If the call to subprocess.run(...) throws a subprocess.CalledProcessError exception, then the result variable is not actually assigned, and so return result is an error because that variable does not exist.

One way to handle this situation is to make sure that result is assigned some default value in the exception case.

Upvotes: 0

Related Questions