diff --git a/config.py b/config.py index a395425..bd7bb10 100644 --- a/config.py +++ b/config.py @@ -5,4 +5,7 @@ LOG_TO_FILE = True LOG_TO_CONSOLE = True LOG_FILE = "log/app.log" -LOG_FORMAT = "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}" \ No newline at end of file +LOG_FORMAT = "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}" +APPLICANT_NAME = "John Doe" +TEX_FILE_NAME = "resume" +TAR_FOLDER_NAME = "resume" \ No newline at end of file diff --git a/envs.py b/envs.py index e757f3e..93c657c 100644 --- a/envs.py +++ b/envs.py @@ -8,6 +8,5 @@ GMAIL_APP_PASSWORD = getenv("GMAIL_APP_PASSWORD") # Put your gmail app password here ADD_GDRIVE_ZAP_URL = getenv("ADD_GDRIVE_ZAP_URL") # Put zappier webhook workflow here, This webhook uploads the file to GDrive LaTeX_COMPILER_URL_TEXT = "https://texlive2020.latexonline.cc/compile?command=pdflatex&text=" -# LaTeX_COMPILER_URL = "https://latexonline.cc/compile?command=xelatex&text=" -LaTeX_COMPILER_URL_DATA = "https://latexonline.cc/data?target=resume/resume.tex&force=true&command=pdflatex" +LaTeX_COMPILER_URL_DATA = """https://latexonline.cc/data?target={tex_folder_path}&force=true&command=pdflatex""" diff --git a/main.py b/main.py index 0df78c7..1ebbd1d 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ from templates import john_doe_resume from log import logger +from config import APPLICANT_NAME app = FastAPI() @@ -43,11 +44,15 @@ def generate_tailored_latex_resume_save(job_description: str, resume: str = john Gets resume and job description in plain text and saves tailored resume """ tailored_plain_resume = generate_tailored_plain_resume(resume, job_description, model, template) - latex_compiler_reponse, _ = openai_wrapper.covert_plain_resume_to_latex(tailored_plain_resume, model, template) company_name = openai_wrapper.ai_prompt(f"Give the name of the company that this job description is for. As the output just give the name, nothing else. Job description: {job_description}") # Since this is a simple task we use the cheapest ai - with open(f'./CVs/{company_name}_cv.pdf', 'wb') as f: + # Make a folder for each job description to save PDF, .tex, and .tar files of tailored resume. + os.makedirs(f'./CVs/{company_name}',exist_ok=True) + latex_compiler_reponse, _ = openai_wrapper.covert_plain_resume_to_latex(company_name, tailored_plain_resume, model, template) + # Path to save pdf file of tailored resume + pdf_path = f'./CVs/{company_name}/{APPLICANT_NAME}_cv.pdf' + with open(pdf_path, 'wb') as f: f.write(latex_compiler_reponse.content) - logger.debug(f"Generated resume saved at here: ./CVs/{company_name}_cv.pdf") - return {"success": f"Generated resume saved at here: ./CVs/{company_name}_cv.pdf", - "path": os.path.abspath(f"./CVs/{company_name}_cv.pdf") + logger.debug(f"Generated resume saved at here: {pdf_path}") + return {"success": f"Generated resume saved at here: {pdf_path}", + "path": os.path.abspath(pdf_path) } diff --git a/openai_wrapper.py b/openai_wrapper.py index 6afae4c..58c830c 100644 --- a/openai_wrapper.py +++ b/openai_wrapper.py @@ -11,6 +11,7 @@ from templates import TemplateName, Template_Details import os from utils import generate_tex_and_tar +from config import TEX_FILE_NAME, TAR_FOLDER_NAME client = OpenAI(api_key=OPEN_AI_KEY) # we recommend using python-dotenv to add OPENAI_API_KEY="My API Key" to your .env file so that your API Key is not stored in source control. @@ -96,7 +97,7 @@ def create_tailored_plain_resume(resume: str, job_description: str, model=AIMode logger.debug(f"The tailored CV plain text is: {tailored_resume}") return tailored_resume -def covert_plain_resume_to_latex(plain_resume: str, model=AIModel.gpt_4o_mini, template=TemplateName.Blue_Modern_CV): +def covert_plain_resume_to_latex(company_name: str, plain_resume: str, model=AIModel.gpt_4o_mini, template=TemplateName.Blue_Modern_CV): messages=[ {"role": "system", "content": "You are a helpful assistant."}, @@ -112,11 +113,10 @@ def covert_plain_resume_to_latex(plain_resume: str, model=AIModel.gpt_4o_mini, t tailored_resume = json.loads(completion.choices[0].message.content)["tailored_resume"] logger.debug(f"The tailored CV Latex code in iteration {i} is: {tailored_resume}") trimed_tailored_resume = tailored_resume[tailored_resume.find(r"\documentclass"):tailored_resume.rfind(r"\end{document}")+len(r"\end{document}")] # removes possible extra things that AI adds - file_name, folder_name = "resume", "resume" - created_tar_file = generate_tex_and_tar(trimed_tailored_resume, f"{file_name}", f"{folder_name}") + created_tar_file = generate_tex_and_tar(company_name, trimed_tailored_resume, TEX_FILE_NAME, TAR_FOLDER_NAME) with open(created_tar_file, 'rb') as tar_file: files = {'file':(os.path.basename(created_tar_file), tar_file, "application/x-tar")} - latex_compiler_response = requests.post(url=LaTeX_COMPILER_URL_DATA, files= files) + latex_compiler_response = requests.post(url=LaTeX_COMPILER_URL_DATA.format(tex_folder_path=f"{TAR_FOLDER_NAME}/{TEX_FILE_NAME}.tex"), files= files) logger.debug(f"Request url to the LaTeX compiler is: {latex_compiler_response.url}") if not b"error: " in latex_compiler_response.content: # there is no error in the compiled code return latex_compiler_response, trimed_tailored_resume diff --git a/utils.py b/utils.py index 304bb70..1edd09b 100644 --- a/utils.py +++ b/utils.py @@ -2,7 +2,7 @@ import tarfile from log import logger -def generate_tex_and_tar(latex_content: str, file_name: str= "resume", folder_name: str="resume"): +def generate_tex_and_tar(company_name: str, latex_content: str, file_name: str= "resume", folder_name: str="resume"): """ Creates a folder, generates a .tex file inside it, and compresses the folder into a .tar file. @@ -12,11 +12,17 @@ def generate_tex_and_tar(latex_content: str, file_name: str= "resume", folder_na folder_name (str): The name of the folder to create. """ try: + # Path of a folder for saving .tex files + resume_folder_path = f'./CVs/{company_name}/{folder_name}' + + # Path of .tar file + tar_path = f'./CVs/{company_name}' + # Ensure the folder exists - os.makedirs(folder_name, exist_ok=True) + os.makedirs(resume_folder_path, exist_ok=True) # Full path for the .tex file - tex_file_path = os.path.join(folder_name, file_name) + tex_file_path = os.path.join(resume_folder_path, file_name) # Ensure the file name ends with .tex if not tex_file_path.endswith(".tex"): @@ -30,11 +36,10 @@ def generate_tex_and_tar(latex_content: str, file_name: str= "resume", folder_na # Compress the folder into a .tar file tar_file_name = f"{folder_name}.tar" - with tarfile.open(tar_file_name, "w") as tar: - tar.add(folder_name, arcname=os.path.basename(folder_name)) - - logger.debug(f"Folder '{folder_name}' compressed into '{tar_file_name}'.") - - return (os.path.relpath(tar_file_name)) + # Full path of .tar folder + tar_folder_path = os.path.join(tar_path, tar_file_name) + with tarfile.open(tar_folder_path, "w") as tar: + tar.add(resume_folder_path, arcname='resume') + return (os.path.relpath(tar_folder_path)) except Exception as e: logger.debug(f"An error occurred: {e}") \ No newline at end of file