Reputation: 1
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
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