some shit

This commit is contained in:
Ireneusz Bachanowicz 2025-03-14 00:59:09 +01:00
parent aadf1fe94c
commit 730a5e7c69
15 changed files with 547 additions and 42 deletions

View File

@ -7,7 +7,9 @@
"build": "next build --no-lint",
"start": "next start",
"lint": "next lint",
"debug": "NODE_DEBUG=next node server.js"
"debug": "NODE_DEBUG=next node server.js",
"test": "pytest utils/tests/test_resume_analysis.py",
"count_documents": "mongosh mongodb://127.0.0.1:27017/cv_summary_db --eval 'db.cv_processing_collection.countDocuments()'"
},
"dependencies": {
"@ai-sdk/google": "^1.1.17",

View File

@ -0,0 +1,87 @@
```json
{
"sections": {
"Summary": {
"score": 8,
"suggestions": [
"Consider adding specific achievements or metrics to highlight impact.",
"Simplify language for clearer understanding."
],
"summary": "The summary provides a clear overview of the candidate's experience and roles in business analysis and IT management but can be improved by adding specific achievements to quantify their contributions.",
"keywords": {
"analityk": 3,
"doświadczenie": 2,
"architekt": 1,
"manager": 1
}
},
"Work Experience": {
"score": 9,
"suggestions": [],
"summary": "The work experience section is detailed, presenting clear job roles, responsibilities, and contributions. It utilizes strong action verbs but could be enhanced with quantifiable results in some roles.",
"keywords": {
"analiz": 5,
"biznesowy": 4,
"systemowy": 4,
"projekt": 4,
"współpraca": 3,
"wymagania": 2
}
},
"Education": {
"score": 8,
"suggestions": [
"Specify the graduation status for higher education.",
"Consider listing any honors or relevant coursework."
],
"summary": "The education section is comprehensive, including degrees and specialized training, but it lacks mention of graduation status and could highlight additional relevant coursework.",
"keywords": {
"Politechnika": 2,
"CISCO": 1,
"Magisterskie": 1,
"Inżynierskie": 1
}
},
"Skills": {
"score": 7,
"suggestions": [
"Categorize skills into technical and soft skills for clarity.",
"Add more specific technologies or methodologies relevant to the roles applied for."
],
"summary": "The skills section is minimal and lacks depth. Categorizing skills can improve clarity and relevance, and including specific technologies or methodologies would strengthen the section.",
"keywords": {
"szkoleń": 4,
"certyfikaty": 2,
"prawo jazdy": 1
}
},
"Certifications": {
"score": 9,
"suggestions": [],
"summary": "The certifications section is strong, detailing relevant training and certifications that add credibility to the candidate's qualifications.",
"keywords": {
"certyfikat": 1,
"szkolenie": 9
}
},
"Projects": {
"score": 6,
"suggestions": [
"Create a separate section for key projects with descriptions and outcomes.",
"Highlight individual contributions to collaborative projects."
],
"summary": "The projects are mentioned informally within work experience; however, creating a dedicated section would better emphasize significant projects and achievements.",
"keywords": {
"projekt": 4,
"wymagania": 2
}
}
},
"openai_stats": {
"input_tokens": 2585,
"output_tokens": 677,
"total_tokens": 3262,
"cost": 0.01308
}
}
```

View File

@ -0,0 +1,19 @@
{
"choices": [
{
"message": {
"content": "```json\n{\n \"sections\": {\n \"Summary\": {\n \"score\": 8,\n \"suggestions\": [\n \"Consider adding specific achievements or metrics to illustrate impact.\",\n \"Make the summary more concise by focusing on key strengths.\"\n ],\n \"summary\": \"The summary provides a brief overview of experience and roles but lacks specific accomplishments and is slightly verbose.\",\n \"keywords\": { \"analityk\": 3, \"doświadczenie\": 2, \"systemowy\": 2, \"technologicznych\": 1, \"menedżer\": 1 }\n },\n \"Work Experience\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The work experience section is detailed and relevant, showcasing roles and responsibilities effectively, with clear job titles and dates.\",\n \"keywords\": { \"analityk\": 4, \"systemów\": 4, \"IT\": 6, \"projekty\": 4, \"współpraca\": 3 }\n },\n \"Education\": {\n \"score\": 7,\n \"suggestions\": [\n \"Provide dates for all educational entries for consistency.\",\n \"Consider adding any relevant coursework or projects to enhance completeness.\"\n ],\n \"summary\": \"The education section lists qualifications but lacks specific dates for every entry and does not include additional relevant details.\",\n \"keywords\": { \"studia\": 3, \"Politechnika\": 3, \"certyfikaty\": 1, \"sieci\": 1 }\n },\n \"Skills\": {\n \"score\": 8,\n \"suggestions\": [\n \"Group skills into categories (e.g., technical skills, soft skills) for clarity.\",\n \"Add specific software or tools to demonstrate technical expertise.\"\n ],\n \"summary\": \"The skills section summarizes capabilities but could benefit from organization and inclusion of specific skills relevant to jobs being applied for.\",\n \"keywords\": { \"techniczne\": 1, \"wiedza\": 1, \"umiejętności\": 1 }\n },\n \"Certifications\": {\n \"score\": 8,\n \"suggestions\": [\n \"Organize certifications in chronological order or by relevance.\",\n \"Include the dates of certifications for better context.\"\n ],\n \"summary\": \"The certifications are relevant but could be polished by adding organization and dates to enhance clarity.\",\n \"keywords\": { \"certyfikat\": 2, \"szkolenie\": 6, \"ITIL\": 2 }\n },\n \"Projects\": {\n \"score\": 6,\n \"suggestions\": [\n \"Provide more detail on individual projects, focusing on specific roles and outcomes.\",\n \"Include dates for project completion to establish a timeline.\"\n ],\n \"summary\": \"The projects section is present but lacks depth regarding specific responsibilities or results, making it less impactful.\",\n \"keywords\": { \"projekt\": 3, \"systemy\": 2, \"migrować\": 1 }\n }\n },\n \"openai_stats\": {\n \"input_tokens\": 1424,\n \"output_tokens\": 668,\n \"total_tokens\": 2092,\n \"cost\": 0.002092\n }\n}\n```",
"role": "assistant"
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 2668,
"completion_tokens": 679,
"total_tokens": 3347
},
"cost": 0,
"model": "gpt-4o-mini-2024-07-18"
}

View File

@ -0,0 +1,19 @@
{
"choices": [
{
"message": {
"content": "```json\n{\n \"sections\": {\n \"Summary\": {\n \"score\": 8,\n \"suggestions\": [\n \"Consider elaborating on specific achievements or key projects to highlight impact.\",\n \"Include more quantifiable metrics to showcase successful outcomes.\"\n ],\n \"summary\": \"The summary provides a clear overview of the candidate's professional background and experience in business analysis and system architecture. It indicates substantial experience but lacks specific examples of accomplishments.\",\n \"keywords\": {\n \"Analityk\": 4,\n \"biznesowy\": 2,\n \"systemowy\": 2,\n \"doświadczenie\": 1,\n \"technologicznych\": 1\n }\n },\n \"Work Experience\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The work experience section is comprehensive, detailing various roles and responsibilities across multiple companies. It demonstrates a strong background in the IT sector with clear responsibilities and contributions but could benefit from more quantifiable outcomes.\",\n \"keywords\": {\n \"analityk\": 6,\n \"systemów\": 5,\n \"projekt\": 4,\n \"współpraca\": 3,\n \"technologii\": 3,\n \"wymagań\": 2,\n \"usług\": 2\n }\n },\n \"Education\": {\n \"score\": 8,\n \"suggestions\": [\n \"Specify the dates for when the education was completed.\",\n \"Only include institutions that are directly relevant to the position being applied for.\"\n ],\n \"summary\": \"The education section lists relevant degrees and institutions, highlighting a solid academic background in technology and information systems. Adding completion dates could enhance clarity.\",\n \"keywords\": {\n \"studia\": 3,\n \"Politechnika\": 2,\n \"informatycznych\": 2,\n \"CISCO\": 1,\n \"specjalność\": 1\n }\n },\n \"Skills\": {\n \"score\": 7,\n \"suggestions\": [\n \"Add more technical skills that are specifically relevant to the industry.\",\n \"Provide a clearer structure, possibly categorizing hard and soft skills.\"\n ],\n \"summary\": \"The skills section is notably brief. It lists language proficiency but lacks a comprehensive enumeration of technical and soft skills essential for the role of a business analyst.\",\n \"keywords\": {\n \"angielski\": 1,\n \"niemiecki\": 1\n }\n },\n \"Certifications\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The certifications are presented clearly, showing a variety of relevant courses and certifications. This indicates a commitment to professional development and continuous learning.\",\n \"keywords\": {\n \"certyfikat\": 2,\n \"szkolenie\": 8,\n \"ITIL\": 2,\n \"IBM\": 3\n }\n },\n \"Projects\": {\n \"score\": 6,\n \"suggestions\": [\n \"Include specific projects with concise descriptions and impacts.\",\n \"List projects in a structured format, summarizing outcomes and key learnings.\"\n ],\n \"summary\": \"The projects section is not explicitly defined and lacks specifics. While detailed experience is found in work experience, this section would benefit from a clear presentation of significant projects and their outcomes.\",\n \"keywords\": {}\n }\n },\n \"openai_stats\": {\n \"input_tokens\": 1244,\n \"output_tokens\": 646,\n \"total_tokens\": 1890,\n \"cost\": 0.002\n }\n}\n```",
"role": "assistant"
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 2668,
"completion_tokens": 760,
"total_tokens": 3428
},
"cost": 0.0,
"model": "gpt-4o-mini-2024-07-18"
}

View File

@ -0,0 +1,19 @@
{
"choices": [
{
"message": {
"content": "```json\n{\n \"sections\": {\n \"Summary\": {\n \"score\": 8,\n \"suggestions\": [\n \"Consider adding specific achievements or metrics to quantify your impact.\",\n \"Refine language to be more concise and impactful.\"\n ],\n \"summary\": \"The summary provides a clear professional profile highlighting experience in business analysis and technology. However, it lacks specific achievements.\",\n \"keywords\": {\n \"Analityk\": 3,\n \"biznesowy\": 3,\n \"systemowy\": 3,\n \"doświadczenie\": 2,\n \"technologicznych\": 1\n }\n },\n \"Work Experience\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The work experience section is comprehensive, detailing roles and responsibilities with an emphasis on contributions to projects. The use of bullet points enhances readability.\",\n \"keywords\": {\n \"analityk\": 4,\n \"programów\": 3,\n \"systemów\": 4,\n \"projektów\": 4,\n \"współpraca\": 3\n }\n },\n \"Education\": {\n \"score\": 8,\n \"suggestions\": [\n \"Specify the completion dates for each education entry.\",\n \"Include any honors or relevant courses to enhance detail.\"\n ],\n \"summary\": \"The education section lists relevant degrees and certifications, but lacks completion dates and additional achievements.\",\n \"keywords\": {\n \"studia\": 3,\n \"Politechnika\": 2,\n \"CISCO\": 1,\n \"certyfikat\": 1\n }\n },\n \"Skills\": {\n \"score\": 7,\n \"suggestions\": [\n \"List specific technical skills or tools you are proficient in.\",\n \"Group skills into categories for improved clarity.\"\n ],\n \"summary\": \"The skills section is minimal and lacks specificity. Adding more detailed skills related to business analysis and technology would be beneficial.\",\n \"keywords\": {\n \"analityka\": 1,\n \"systemowy\": 1\n }\n },\n \"Certifications\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The certifications section is well-detailed and relevant, showcasing important qualifications for the field.\",\n \"keywords\": {\n \"certyfikat\": 1,\n \"szkolenie\": 5\n }\n },\n \"Projects\": {\n \"score\": 6,\n \"suggestions\": [\n \"Add specific project names and outcomes to illustrate contributions.\",\n \"Include metrics or results achieved in projects.\"\n ],\n \"summary\": \"The projects section is lacking, as it does not list projects explicitly or specify contributions. More detail could improve understanding of expertise.\",\n \"keywords\": {\n \"projekt\": 1,\n \"analiz\": 1\n }\n }\n },\n \"openai_stats\": {\n \"input_tokens\": 1318,\n \"output_tokens\": 509,\n \"total_tokens\": 1827,\n \"cost\": 0.002053\n }\n}\n```",
"role": "assistant"
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 2668,
"completion_tokens": 658,
"total_tokens": 3326
},
"cost": 0,
"model": "gpt-4o-mini-2024-07-18"
}

View File

@ -0,0 +1,19 @@
{
"choices": [
{
"message": {
"content": "```json\n{\n \"sections\": {\n \"Summary\": {\n \"score\": 8,\n \"suggestions\": [\"Add specific metrics to quantify achievements.\", \"Clarify the type of industries and roles you are most experienced in.\"],\n \"summary\": \"The summary provides a brief professional profile, emphasizing business and system analysis experience. However, it lacks specific metrics or examples of achievements.\",\n \"keywords\": {\n \"analityk\": 3,\n \"doświadczenie\": 2,\n \"systemowy\": 2,\n \"architekt\": 1,\n \"manager\": 1\n }\n },\n \"Work Experience\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The work experience section is comprehensive, detailing roles, responsibilities, and projects. Each role is clearly delineated, showcasing relevant experience and contributions.\",\n \"keywords\": {\n \"analityk\": 5,\n \"system\": 4,\n \"projekt\": 4,\n \"zespół\": 2,\n \"usługi\": 3,\n \"współpraca\": 2\n }\n },\n \"Education\": {\n \"score\": 8,\n \"suggestions\": [\"Add graduation dates for each educational experience.\", \"Clearly specify the fields of study.\"],\n \"summary\": \"The education section provides various qualifications, but it could benefit from specific graduation dates and clarification of study fields.\",\n \"keywords\": {\n \"Politechnika\": 2,\n \"studia\": 3,\n \"CISCO\": 1,\n \"magisterskie\": 1,\n \"inżynierskie\": 1\n }\n },\n \"Skills\": {\n \"score\": 7,\n \"suggestions\": [\"List both hard and soft skills explicitly.\", \"Include any technical skills relevant to the roles applied for.\"],\n \"summary\": \"The skills section needs improvement; it lacks a clear list of both hard and soft skills that could enhance the individual's candidacy.\",\n \"keywords\": {\n \"CRM\": 2,\n \"analiza\": 2,\n \"zrozumienie\": 1,\n \"systemowy\": 1,\n \"projektowanie\": 1\n }\n },\n \"Certifications\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The certifications section is strong with relevant certifications listed, demonstrating a commitment to professional development.\",\n \"keywords\": {\n \"certyfikat\": 2,\n \"ITIL\": 2,\n \"szkolenie\": 5,\n \"IBM\": 3\n }\n },\n \"Projects\": {\n \"score\": 7,\n \"suggestions\": [\"Add more details about specific projects (e.g., outcomes, skills used).\", \"Highlight any leadership roles in projects.\"],\n \"summary\": \"The projects section is present but lacks depth; it could highlight key achievements and the impact of each project.\",\n \"keywords\": {\n \"projekt\": 4,\n \"systemowy\": 2,\n \"analiza\": 1,\n \"zespół\": 2\n }\n }\n },\n \"openai_stats\": {\n \"input_tokens\": 1291,\n \"output_tokens\": 566,\n \"total_tokens\": 1857,\n \"cost\": 0.004\n }\n}\n```",
"role": "assistant"
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 2668,
"completion_tokens": 720,
"total_tokens": 3388
},
"cost": 0.0,
"model": "gpt-4o-mini-2024-07-18"
}

View File

@ -0,0 +1,19 @@
{
"choices": [
{
"message": {
"content": "```json\n{\n \"sections\": {\n \"Summary\": {\n \"score\": 8,\n \"suggestions\": [\n \"Make the summary more concise by focusing on key skills and achievements.\",\n \"Add specific examples of business analysis and architecture achievements.\"\n ],\n \"summary\": \"Strong professional summary indicating a solid background in business and system analysis with over 10 years of relevant experience, but lacks specific accomplishments.\",\n \"keywords\": {\n \"business analyst\": 1,\n \"system architect\": 1,\n \"manager\": 1,\n \"experience\": 1\n }\n },\n \"Work Experience\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"Detailed work experience in various roles with a focus on business analysis and IT management. Effective descriptions of responsibilities and contributions, although some job roles could highlight specific achievements more clearly.\",\n \"keywords\": {\n \"business analysis\": 5,\n \"system\": 6,\n \"IT\": 4,\n \"project\": 2,\n \"analysis\": 3,\n \"documentation\": 2\n }\n },\n \"Education\": {\n \"score\": 8,\n \"suggestions\": [\n \"Specify graduation dates for each educational qualification.\",\n \"Include any honors or distinctions received during studies.\"\n ],\n \"summary\": \"Solid educational background with relevant degrees and certifications in technology and electronics, but lacks detail on specific achievements or honors.\",\n \"keywords\": {\n \"degree\": 3,\n \"education\": 2,\n \"network associate\": 1\n }\n },\n \"Skills\": {\n \"score\": 7,\n \"suggestions\": [\n \"Expand on the range of technical and soft skills relevant to the positions sought.\",\n \"Organize skills into categories (e.g., Technical, Analytical, Interpersonal) for better clarity.\"\n ],\n \"summary\": \"Skills listed are somewhat general; better categorization and specificity could improve overall relevance.\",\n \"keywords\": {\n \"skills\": 1,\n \"analysis\": 2,\n \"communication\": 1\n }\n },\n \"Certifications\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The section is well-structured and lists relevant certifications clearly, showcasing continuous professional development.\",\n \"keywords\": {\n \"certification\": 1,\n \"ITIL\": 1,\n \"CISCO\": 1\n }\n },\n \"Projects\": {\n \"score\": 6,\n \"suggestions\": [\n \"Provide more specific details about project outcomes or impacts.\",\n \"Highlight personal contributions or leadership roles in notable projects.\"\n ],\n \"summary\": \"Projects are mentioned but lack depth regarding impact and individual contributions. More concrete successes would strengthen the narrative.\",\n \"keywords\": {\n \"project\": 3,\n \"migration\": 1,\n \"implementation\": 1\n }\n }\n },\n \"openai_stats\": {\n \"input_tokens\": 2155,\n \"output_tokens\": 722,\n \"total_tokens\": 2877,\n \"cost\": 0.002877\n }\n}\n```",
"role": "assistant"
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 2668,
"completion_tokens": 668,
"total_tokens": 3336
},
"cost": 0.0,
"model": "gpt-4o-mini-2024-07-18"
}

View File

@ -0,0 +1,19 @@
{
"choices": [
{
"message": {
"content": "```json\n{\n \"sections\": {\n \"Summary\": {\n \"score\": 8,\n \"suggestions\": [\"Consider including specific achievements or metrics to highlight your impact.\", \"Make the language more concise and powerful.\"],\n \"summary\": \"The summary provides a clear overview of the candidate's role and experience but lacks specific accomplishments that could strengthen it.\",\n \"keywords\": { \"Analityk\": 2, \"doświadczenie\": 1, \"manager\": 1, \"architekt\": 1 }\n },\n \"Work Experience\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The work experience section is detailed and comprehensive, showcasing a strong career progression and relevant expertise in various roles.\",\n \"keywords\": { \"IT\": 6, \"analityk\": 5, \"systemów\": 5, \"projekt\": 5, \"współpraca\": 4, \"klientów\": 3, \"usług\": 3 }\n },\n \"Education\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"Education section is informative and highlights relevant degrees and certifications, showcasing the candidate's academic background.\",\n \"keywords\": { \"studia\": 3, \"Politechnika Warszawska\": 2, \"CISCO\": 1, \"Magister\": 1, \"Inżynierskie\": 1 }\n },\n \"Skills\": {\n \"score\": 7,\n \"suggestions\": [\"List skills specifically related to the positions applied for.\", \"Consider organizing skills into relevant categories.\"],\n \"summary\": \"Skills section is not explicitly defined, making it difficult to quickly assess the candidate's qualifications. Specific skills and categories would add clarity.\",\n \"keywords\": { \"analiza\": 2, \"systemy\": 1, \"współpraca\": 1, \"usługi\": 1 }\n },\n \"Certifications\": {\n \"score\": 8,\n \"suggestions\": [\"Add the date for each certification obtained for better clarity.\", \"Consider grouping certifications by relevance.\"],\n \"summary\": \"The certifications section lists various relevant training and qualifications but would benefit from more organization and specificity.\",\n \"keywords\": { \"certyfikat\": 1, \"szkolenie\": 1, \"ITIL\": 2 }\n },\n \"Projects\": {\n \"score\": 7,\n \"suggestions\": [\"Include specific project names and outcomes to enhance detail.\", \"Highlight individual contributions more clearly.\"],\n \"summary\": \"The projects section provides some context but lacks clear delineation of specific projects or the candidate's individual contributions and results.\",\n \"keywords\": { \"projekt\": 3, \"współpraca\": 2, \"systemy\": 1 }\n }\n },\n \"openai_stats\": {\n \"input_tokens\": 1526,\n \"output_tokens\": 469,\n \"total_tokens\": 1995,\n \"cost\": 0.09975\n }\n}\n```",
"role": "assistant"
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 2668,
"completion_tokens": 647,
"total_tokens": 3315
},
"cost": 0.0,
"model": "gpt-4o-mini-2024-07-18"
}

View File

@ -0,0 +1,19 @@
{
"choices": [
{
"message": {
"content": "```json\n{\n \"sections\": {\n \"Summary\": {\n \"score\": 8,\n \"suggestions\": [\n \"Consider adding specific achievements or metrics to highlight impact.\",\n \"Simplify language for clearer understanding.\"\n ],\n \"summary\": \"The summary provides a clear overview of the candidate's experience and roles in business analysis and IT management but can be improved by adding specific achievements to quantify their contributions.\",\n \"keywords\": {\n \"analityk\": 3,\n \"doświadczenie\": 2,\n \"architekt\": 1,\n \"manager\": 1\n }\n },\n \"Work Experience\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The work experience section is detailed, presenting clear job roles, responsibilities, and contributions. It utilizes strong action verbs but could be enhanced with quantifiable results in some roles.\",\n \"keywords\": {\n \"analiz\": 5,\n \"biznesowy\": 4,\n \"systemowy\": 4,\n \"projekt\": 4,\n \"współpraca\": 3,\n \"wymagania\": 2\n }\n },\n \"Education\": {\n \"score\": 8,\n \"suggestions\": [\n \"Specify the graduation status for higher education.\",\n \"Consider listing any honors or relevant coursework.\"\n ],\n \"summary\": \"The education section is comprehensive, including degrees and specialized training, but it lacks mention of graduation status and could highlight additional relevant coursework.\",\n \"keywords\": {\n \"Politechnika\": 2,\n \"CISCO\": 1,\n \"Magisterskie\": 1,\n \"Inżynierskie\": 1\n }\n },\n \"Skills\": {\n \"score\": 7,\n \"suggestions\": [\n \"Categorize skills into technical and soft skills for clarity.\",\n \"Add more specific technologies or methodologies relevant to the roles applied for.\"\n ],\n \"summary\": \"The skills section is minimal and lacks depth. Categorizing skills can improve clarity and relevance, and including specific technologies or methodologies would strengthen the section.\",\n \"keywords\": {\n \"szkoleń\": 4,\n \"certyfikaty\": 2,\n \"prawo jazdy\": 1\n }\n },\n \"Certifications\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The certifications section is strong, detailing relevant training and certifications that add credibility to the candidate's qualifications.\",\n \"keywords\": {\n \"certyfikat\": 1,\n \"szkolenie\": 9\n }\n },\n \"Projects\": {\n \"score\": 6,\n \"suggestions\": [\n \"Create a separate section for key projects with descriptions and outcomes.\",\n \"Highlight individual contributions to collaborative projects.\"\n ],\n \"summary\": \"The projects are mentioned informally within work experience; however, creating a dedicated section would better emphasize significant projects and achievements.\",\n \"keywords\": {\n \"projekt\": 4,\n \"wymagania\": 2\n }\n }\n },\n \"openai_stats\": {\n \"input_tokens\": 2585,\n \"output_tokens\": 677,\n \"total_tokens\": 3262,\n \"cost\": 0.01308\n }\n}\n```",
"role": "assistant"
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 2668,
"completion_tokens": 706,
"total_tokens": 3374
},
"cost": 0.0,
"model": "gpt-4o-mini-2024-07-18"
}

View File

@ -0,0 +1,19 @@
{
"choices": [
{
"message": {
"content": "```json\n{\n \"sections\": {\n \"Summary\": {\n \"score\": 8,\n \"suggestions\": [\n \"Add specific accomplishments or metrics to demonstrate impact\",\n \"Consider using bullet points for easier readability\"\n ],\n \"summary\": \"The summary provides a clear overview of the candidate's experience and roles as a business analyst, architect, and manager. However, it lacks specific details on achievements or metrics that could enhance its impact.\",\n \"keywords\": {\n \"Analityk biznesowy\": 1,\n \"systemowy\": 1,\n \"architekt\": 1,\n \"manager\": 1,\n \"doświadczenie\": 1\n }\n },\n \"Work Experience\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"This section provides comprehensive details about the candidate's relevant work experience, including roles, responsibilities, and achievements. It is well-structured and effectively highlights the candidates expertise.\",\n \"keywords\": {\n \"analityk\": 5,\n \"systemowy\": 2,\n \"kierownik\": 2,\n \"dzieło\": 2,\n \"projekt\": 3,\n \"współpraca\": 2,\n \"systemy\": 3,\n \"dokumentacja\": 2\n }\n },\n \"Education\": {\n \"score\": 8,\n \"suggestions\": [\n \"Include graduation years for better context\",\n \"Consider adding any honors or relevant coursework\"\n ],\n \"summary\": \"The education section lists relevant degrees and certifications, but lacks graduation dates and specifics about honors which could strengthen the presentation.\",\n \"keywords\": {\n \"Magisterskie\": 1,\n \"Inżynierskie\": 1,\n \"Politechnika\": 2,\n \"CISCO\": 1,\n \"specjalność\": 3\n }\n },\n \"Skills\": {\n \"score\": 7,\n \"suggestions\": [\n \"Add more specific technical and soft skills\",\n \"Group skills into categories for clarity\"\n ],\n \"summary\": \"The skills section is brief and could benefit from more detail. Including specific technical skills, soft skills, and grouping them would enhance this sections effectiveness.\",\n \"keywords\": {}\n },\n \"Certifications\": {\n \"score\": 9,\n \"suggestions\": [],\n \"summary\": \"The certifications section is well-detailed, showcasing a range of relevant training and certifications that support the candidate's qualifications. No improvements needed.\",\n \"keywords\": {\n \"certyfikat\": 3,\n \"szkolenie\": 6,\n \"ITIL\": 2\n }\n },\n \"Projects\": {\n \"score\": 8,\n \"suggestions\": [\n \"Provide more detailed descriptions of key projects\",\n \"Highlight any specific outcomes or results achieved\"\n ],\n \"summary\": \"The projects section includes relevant experiences but would be improved by elaborating on the specifics of projects and their outcomes, including metrics or achievements.\",\n \"keywords\": {\n \"projekt\": 4,\n \"analiza\": 2,\n \"współpraca\": 1\n }\n }\n },\n \"openai_stats\": {\n \"input_tokens\": 1695,\n \"output_tokens\": 712,\n \"total_tokens\": 2407,\n \"cost\": 0.0035\n }\n}\n```",
"role": "assistant"
},
"finish_reason": "stop",
"index": 0
}
],
"usage": {
"prompt_tokens": 2668,
"completion_tokens": 729,
"total_tokens": 3397
},
"cost": 0,
"model": "gpt-4o-mini-2024-07-18"
}

View File

@ -59,10 +59,11 @@ Required Environment Variables:
- MONGODB_URI: MongoDB connection string (optional for mockup mode)""",
usage="resume_analysis.py [-h] [-f FILE] [-m]",
epilog="""Examples:
Analyze a resume: resume_analysis.py -f my_resume.pdf
Test with mockup data: resume_analysis.py -f test.pdf -m"""
Analyze a resume: resume_analysis.py -f my_resume.txt
Test with mockup data: resume_analysis.py -f test.txt -m"""
)
parser.add_argument('-f', '--file', help='Path to the resume file to analyze (PDF or text)')
parser.add_argument('-f', '--file', help='Path to the resume file to analyze (TXT)')
parser.add_argument('-p', '--pdf', help='Path to the resume file to analyze (PDF)')
parser.add_argument('-m', '--mockup', action='store_true', help='Use mockup response instead of calling OpenAI API')
# If no arguments provided, show help and exit
@ -79,21 +80,48 @@ Required Environment Variables:
if use_mockup:
resume_text = "Mockup resume text"
else:
if not os.path.exists(args.file):
logger.error(f"File not found: {args.file}")
if args.pdf:
if not os.path.exists(args.pdf):
logger.error(f"PDF file not found: {args.pdf}")
sys.exit(1)
start_file_read_time = time.time()
try:
resume_text = extract_text(args.pdf)
except Exception as e:
logger.error(f"Error extracting text from PDF: {e}", exc_info=True)
sys.exit(1)
file_read_time = time.time() - start_file_read_time
logger.debug(f"PDF file read time: {file_read_time:.2f} seconds")
# Save extracted text to file
pdf_filename = os.path.splitext(os.path.basename(args.pdf))[0]
text_file_path = os.path.join(os.path.dirname(args.pdf), f"{pdf_filename}_text.txt")
with open(text_file_path, "w", encoding="utf-8") as text_file:
text_file.write(resume_text)
logger.debug(f"Extracted text saved to: {text_file_path}")
elif args.file:
if not os.path.exists(args.file):
logger.error(f"File not found: {args.file}")
sys.exit(1)
start_file_read_time = time.time()
with open(args.file, 'r', encoding='latin-1') as f:
resume_text = f.read()
file_read_time = time.time() - start_file_read_time
logger.debug(f"File read time: {file_read_time:.2f} seconds")
else:
parser.print_help()
sys.exit(1)
start_file_read_time = time.time()
with open(args.file, 'r') as f:
resume_text = f.read()
file_read_time = time.time() - start_file_read_time
logger.debug(f"File read time: {file_read_time:.2f} seconds")
# Call the OpenAI API with the resume text
start_time = time.time()
response = call_openai_api(resume_text, use_mockup)
openai_api_time = time.time() - start_time
logger.debug(f"OpenAI API call time: {openai_api_time:.2f} seconds")
try:
response = call_openai_api(resume_text, use_mockup)
openai_api_time = time.time() - start_time
logger.debug(f"OpenAI API call time: {openai_api_time:.2f} seconds")
except Exception as e:
logger.error(f"Error during OpenAI API call: {e}", exc_info=True)
response = None
# Initialize MongoDB collection only when needed
cv_collection = get_mongo_collection()
@ -111,7 +139,7 @@ def load_mockup_response(mockup_file_path: str) -> dict:
raise FileNotFoundError(f"Mockup file not found at: {mockup_file_path}")
with open(mockup_file_path, "r") as f:
response = json.load(f)
response.setdefault("openai_stats", {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0})
#response.setdefault("openai_stats", {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0})
return response
def call_openai_api(text: str, use_mockup: bool) -> Optional[Any]:
@ -119,7 +147,7 @@ def call_openai_api(text: str, use_mockup: bool) -> Optional[Any]:
logger.debug("Calling OpenAI API.")
try:
if use_mockup:
return load_mockup_response(MOCKUP_FILE_PATH)
return load_mockup_response(os.path.join(os.path.dirname(__file__), 'tests', 'mockup_response.json'))
with open(os.path.join(os.path.dirname(__file__), "prompt.txt"), "r") as prompt_file:
system_content = prompt_file.read()
@ -138,20 +166,35 @@ def call_openai_api(text: str, use_mockup: bool) -> Optional[Any]:
logger.error(f"Error during OpenAI API call: {e}", exc_info=True)
return None
def write_openai_response(response: Any, use_mockup: bool, input_file_path: str = None, cost: float = 0) -> None: # Add cost argument
def write_openai_response(response: Any, use_mockup: bool, input_file_path: str = None, cost: float = 0) -> None:
"""Write raw OpenAI response to a file."""
if use_mockup:
logger.debug("Using mockup response; no OpenAI message to write.")
return
if response and response.choices: # Changed from hasattr to direct attribute access
if response and response.choices:
message_content = response.choices[0].message.content
logger.debug(f"Raw OpenAI message content: {message_content}")
output_dir = os.path.dirname(input_file_path) if input_file_path else '.'
base_filename = os.path.splitext(os.path.basename(input_file_path))[0] if input_file_path else "default"
if input_file_path:
output_dir = os.path.dirname(input_file_path)
base_filename = os.path.splitext(os.path.basename(input_file_path))[0]
else:
logger.warning("Input file path not provided. Using default output directory and filename.")
output_dir = os.path.join(os.path.dirname(__file__)) # Default to script's directory
base_filename = "default" # Default filename
processing_id = str(uuid.uuid4())
file_path = os.path.join(output_dir, f"{base_filename}_openai_response_{processing_id}") + ".json"
openai_file_path = os.path.join(output_dir, f"{base_filename}_openai.txt")
try:
serializable_response = { # Create a serializable dictionary
message_content = response.choices[0].message.content if response and response.choices else "No content"
with open(openai_file_path, "w", encoding="utf-8") as openai_file:
openai_file.write(message_content)
logger.debug(f"OpenAI response written to {openai_file_path}")
serializable_response = {
"choices": [
{
"message": {
@ -162,46 +205,64 @@ def write_openai_response(response: Any, use_mockup: bool, input_file_path: str
"index": choice.index
} for choice in response.choices
],
"openai_stats": {
"usage": {
"prompt_tokens": response.usage.prompt_tokens,
"completion_tokens": response.usage.completion_tokens,
"total_tokens": response.usage.total_tokens
},
"cost": cost, # Include cost in the output JSON
},
"cost": cost, # Include cost in the output JSON
"model": response.model
}
with open(file_path, "w") as f:
json.dump(serializable_response, f, indent=2) # Dump the serializable dictionary
json.dump(serializable_response, f, indent=2, ensure_ascii=False)
logger.debug(f"OpenAI response written to {file_path}")
except IOError as e:
logger.error(f"Failed to write OpenAI response to file: {e}")
else:
logger.warning("No choices in OpenAI response to extract message from.")
logger.debug(f"Response object: {response}")
def insert_processing_data(text_content: str, summary: dict, response: Any, args: argparse.Namespace, processing_id: str, use_mockup: bool, cv_collection) -> None:
def insert_processing_data(text_content: str, summary: dict, response: Any, args: argparse.Namespace, processing_id: str, use_mockup: bool, cv_collection) -> float:
"""Insert processing data into MongoDB."""
logger.debug("Inserting processing data into MongoDB.")
cost = 0.0 # Initialize cost to 0.0
if not use_mockup:
if response and response.choices:
message_content = response.choices[0].message.content
openai_stats = {} # Initialize openai_stats
try:
openai_stats_content = json.loads(message_content)
# Attempt to decode JSON, handling potential decode errors
openai_stats_content = json.loads(message_content.encode('utf-8').decode('unicode_escape'))
openai_stats = openai_stats_content.get("openai_stats", {})
cost = openai_stats.get("cost", 0)
except json.JSONDecodeError:
logger.error("Failed to decode JSON from message content for openai_stats.")
openai_stats = {}
cost = 0
cost = openai_stats.get("cost", 0.0)
except json.JSONDecodeError as e:
logger.error(f"JSONDecodeError in message_content: {e}", exc_info=True)
cost = 0.0
except AttributeError as e:
logger.error(f"AttributeError accessing openai_stats: {e}", exc_info=True)
cost = 0.0
except Exception as e:
logger.error(f"Unexpected error extracting cost: {e}", exc_info=True)
cost = 0.0
except AttributeError as e:
logger.error(f"AttributeError when accessing openai_stats or cost: {e}", exc_info=True)
cost = 0.0
try:
usage = response.usage
input_tokens = usage.prompt_tokens
output_tokens = usage.completion_tokens
total_tokens = usage.total_tokens
except Exception as e:
logger.error(f"Error extracting usage data: {e}", exc_info=True)
input_tokens = output_tokens = total_tokens = 0
usage = response.usage
input_tokens = usage.prompt_tokens
output_tokens = usage.completion_tokens
total_tokens = usage.total_tokens
else:
logger.error("Invalid response format or missing usage data.")
input_tokens = output_tokens = total_tokens = 0
cost = 0
cost = 0.0
openai_stats = {}
usage = {}
@ -214,9 +275,6 @@ def insert_processing_data(text_content: str, summary: dict, response: Any, args
"usage_prompt_tokens": input_tokens, # Renamed to avoid collision
"usage_completion_tokens": output_tokens, # Renamed to avoid collision
"usage_total_tokens": total_tokens, # Renamed to avoid collision
"openai_stats_input_tokens": openai_stats.get("input_tokens"),
"openai_stats_output_tokens": openai_stats.get("output_tokens"),
"openai_stats_total_tokens": openai_stats.get("total_tokens"),
"cost": cost
}
@ -228,7 +286,7 @@ def insert_processing_data(text_content: str, summary: dict, response: Any, args
logger.error(f"Failed to insert processing data into MongoDB: {e}", exc_info=True)
else:
logger.debug("Using mockup; skipping MongoDB insertion.")
return 0 # Return 0 for mockup mode
return cost # Return 0 for mockup mode
if __name__ == "__main__":
main()

View File

@ -0,0 +1,174 @@
import os
import sys
import pytest
from unittest.mock import patch, MagicMock
import json
import logging
import argparse # Import argparse
from dotenv import load_dotenv
# Add the project root to the sys path to allow imports from the main package
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from resume_analysis import (
call_openai_api,
insert_processing_data,
load_mockup_response,
main,
get_mongo_collection
)
# Load environment variables for testing
load_dotenv()
# Constants for Mocking
MOCKUP_FILE_PATH = os.path.join(os.path.dirname(__file__), 'mockup_response.json')
TEST_RESUME_PATH = os.path.join(os.path.dirname(__file__), 'test_resume.txt')
# Create a logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# Create a handler and set the formatter
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
# Add the handler to the logger
logger.addHandler(ch)
# Mockup response data
MOCKUP_RESPONSE_DATA = {
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-3.5-turbo-0301",
"usage": {
"prompt_tokens": 100,
"completion_tokens": 200,
"total_tokens": 300
},
"choices": [
{
"message": {
"role": "assistant",
"content": '{"openai_stats": {"prompt_tokens": 100, "completion_tokens": 200, "total_tokens": 300}}'
},
"finish_reason": "stop",
"index": 0
}
]
}
# Fixtures
@pytest.fixture
def mock_openai_response():
mock_response = MagicMock()
mock_response.id = "chatcmpl-123"
mock_response.object = "chat.completion"
mock_response.created = 1677652288
mock_response.model = "gpt-3.5-turbo-0301"
mock_response.usage = MagicMock(prompt_tokens=100, completion_tokens=200, total_tokens=300)
mock_response.choices = [MagicMock(message=MagicMock(role="assistant", content='{"openai_stats": {"prompt_tokens": 100, "completion_tokens": 200, "total_tokens": 300}}'), finish_reason="stop", index=0)]
return mock_response
@pytest.fixture
def test_resume_file():
# Create a dummy resume file for testing
with open(TEST_RESUME_PATH, 'w') as f:
f.write("This is a test resume.")
yield TEST_RESUME_PATH
os.remove(TEST_RESUME_PATH)
@pytest.fixture
def mock_mongo_collection():
# Mock MongoDB collection for testing
class MockMongoCollection:
def __init__(self):
self.inserted_data = None
def insert_one(self, data):
self.inserted_data = data
return MockMongoCollection()
# Unit Tests
def test_load_mockup_response():
# Create a mockup response file
with open(MOCKUP_FILE_PATH, 'w') as f:
json.dump(MOCKUP_RESPONSE_DATA, f)
response = load_mockup_response(MOCKUP_FILE_PATH)
assert response == MOCKUP_RESPONSE_DATA
os.remove(MOCKUP_FILE_PATH)
def test_load_mockup_response_file_not_found():
with pytest.raises(FileNotFoundError):
load_mockup_response("non_existent_file.json")
@patch("resume_analysis.openai.chat.completions.create")
def test_call_openai_api_success(mock_openai_chat_completions_create, mock_openai_response):
mock_openai_chat_completions_create.return_value = mock_openai_response
response = call_openai_api("test resume text", False)
assert response == mock_openai_response
@patch("resume_analysis.openai.chat.completions.create")
def test_call_openai_api_failure(mock_openai_chat_completions_create):
mock_openai_chat_completions_create.side_effect = Exception("API error")
response = call_openai_api("test resume text", False)
assert response is None
def test_call_openai_api_mockup_mode():
# Create a mockup response file
with open(MOCKUP_FILE_PATH, 'w') as f:
json.dump(MOCKUP_RESPONSE_DATA, f)
response = call_openai_api("test resume text", True)
assert response == MOCKUP_RESPONSE_DATA
os.remove(MOCKUP_FILE_PATH)
def test_insert_processing_data_success(mock_openai_response, mock_mongo_collection):
args = argparse.Namespace(file="test.pdf")
cost = insert_processing_data("test resume text", {}, mock_openai_response, args, "test_id", False, mock_mongo_collection)
assert mock_mongo_collection.inserted_data is not None
assert cost == 0
def test_insert_processing_data_mockup_mode(mock_mongo_collection):
args = argparse.Namespace(file="test.pdf")
cost = insert_processing_data("test resume text", {}, MOCKUP_RESPONSE_DATA, args, "test_id", True, mock_mongo_collection)
assert mock_mongo_collection.inserted_data is None
assert cost == 0
@patch("resume_analysis.get_mongo_collection")
def test_main_success(mock_get_mongo_collection, test_resume_file, mock_openai_response):
mock_get_mongo_collection.return_value.insert_one.return_value = None
with patch("resume_analysis.call_openai_api") as mock_call_openai_api:
mock_call_openai_api.return_value = mock_openai_response
with patch("resume_analysis.write_openai_response") as mock_write_openai_response:
sys.argv = ["resume_analysis.py", "-f", test_resume_file]
main()
assert mock_call_openai_api.called
assert mock_write_openai_response.called
@patch("resume_analysis.get_mongo_collection")
def test_main_mockup_mode(mock_get_mongo_collection, test_resume_file, mock_openai_response):
mock_get_mongo_collection.return_value.insert_one.return_value = None
with patch("resume_analysis.call_openai_api") as mock_call_openai_api:
mock_call_openai_api.return_value = mock_openai_response
with patch("resume_analysis.write_openai_response") as mock_write_openai_response:
sys.argv = ["resume_analysis.py", "-f", test_resume_file, "-m"]
main()
assert mock_call_openai_api.called
assert mock_write_openai_response.called
def test_main_file_not_found():
with pytest.raises(SystemExit) as pytest_wrapped_e:
sys.argv = ["resume_analysis.py", "-f", "non_existent_file.pdf"]
main()
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
def test_get_mongo_collection():
# Test that the function returns a valid MongoDB collection object
collection = get_mongo_collection()
assert collection is not None

32
plan.md Normal file
View File

@ -0,0 +1,32 @@
# Plan for Modifying resume_analysis.py
## Objective
Modify the `my-app/utils/resume_analysis.py` script to save the extracted text from a PDF file and the OpenAI response to separate text files, with filenames derived from the original PDF's basename.
## Steps
1. **Examine `resume_analysis.py`:** Read the file to understand the existing PDF processing logic and how the OpenAI response is handled.
2. **Clarify Naming Convention:** Confirm the exact naming convention for the output files.
3. **Implement Changes:** Modify the script to:
* Extract the PDF's basename.
* Save the extracted text to a file named `basename._text.txt` in the same directory as the PDF.
* Save the OpenAI response to a file named `basename_openai.txt` in the same directory.
4. **Test:** Ensure that the changes work correctly for different PDF files and that the output files are created with the correct content and naming.
5. **Create a Plan File:** Create a markdown file with the plan.
6. **Switch Mode:** Switch to code mode to implement the changes.
## Mermaid Diagram
```mermaid
graph LR
A[Start] --> B{Examine resume_analysis.py};
B --> C{Clarify Naming Convention};
C --> D{Modify Script};
D --> E{Extract PDF Basename};
E --> F{Save Extracted Text};
F --> G{Save OpenAI Response};
G --> H{Test Changes};
H --> I{Create Plan File};
I --> J{Switch to Code Mode};
J --> K[End];