kedar-bhumkar commited on
Commit
36652ef
·
verified ·
1 Parent(s): c031f85

Upload 19 files

Browse files
AI_Resume.docx ADDED
Binary file (56.5 kB). View file
 
Academic_Template.docx ADDED
Binary file (37.5 kB). View file
 
Modern_Template.docx ADDED
Binary file (37.5 kB). View file
 
Professional_Template.docx ADDED
Binary file (37.3 kB). View file
 
README.md CHANGED
@@ -1,14 +1,58 @@
1
  ---
2
- title: AI Resume Helper
3
- emoji: 📉
4
- colorFrom: gray
5
- colorTo: gray
6
- sdk: gradio
7
- sdk_version: 5.20.1
8
  app_file: app.py
9
  pinned: false
10
- license: apache-2.0
11
- short_description: AI powered resume analyzer/creator
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Resume Helper
3
+ emoji: 📄
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: streamlit
7
+ sdk_version: 1.28.0
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
 
11
  ---
12
 
13
+ # Resume Helper
14
+
15
+ ## Description
16
+
17
+ Resume Helper is an AI-powered application that helps you tailor your resume to specific job descriptions. Using OpenAI's GPT-4o model, it analyzes your resume against job requirements, provides a match percentage, identifies gaps, and suggests improvements. You can also generate a tailored version of your resume optimized for the specific job.
18
+
19
+ ## Features
20
+
21
+ - **Resume Analysis**: Get a detailed analysis of how well your resume matches a job description
22
+ - **Resume Tailoring**: Generate a customized version of your resume for specific job applications
23
+ - **Match Percentage**: See a quantitative score of how well your resume matches the job requirements
24
+ - **Skill Breakdown**: View a detailed breakdown of your technical skills, experience, education, and soft skills
25
+ - **Gap Identification**: Identify missing skills or experiences that are important for the job
26
+ - **Improvement Suggestions**: Get actionable suggestions to improve your resume
27
+ - **Template Selection**: Choose from various resume templates
28
+ - **Verbosity Control**: Select between concise or detailed resume outputs
29
+ - **Creativity Control**: Adjust how creative the AI should be when tailoring your resume
30
+ - **Sample Resumes**: Use provided sample resumes to test the application
31
+
32
+ ## How to Use
33
+
34
+ 1. **API Key**: Enter your OpenAI API key in the sidebar (or use environment variables)
35
+ 2. **Upload Resume**: Upload your resume in DOCX or PDF format, or select a sample resume
36
+ 3. **Job Description**: Enter the job description you're applying for
37
+ 4. **Template (Optional)**: Select a resume template if desired
38
+ 5. **Verbosity**: Choose between "Concise" or "Elaborate" for your tailored resume
39
+ 6. **Creativity Level**: Adjust how creative the AI should be (0-100%)
40
+ 7. **Analyze**: Click "Analyze Resume" to get a detailed analysis
41
+ 8. **Tailor**: Click "Tailor Resume" to generate a customized version of your resume
42
+ 9. **Download**: Download your tailored resume in DOCX or TXT format
43
+
44
+ ## Limitations
45
+
46
+ - The quality of analysis and tailoring depends on the quality of your resume and the job description
47
+ - Higher creativity levels may generate content that requires verification
48
+ - Always review AI-generated content before using it professionally
49
+
50
+ ## Requirements
51
+
52
+ - Python 3.8+
53
+ - OpenAI API key
54
+ - Required Python packages (see requirements.txt)
55
+
56
+ ## License
57
+
58
+ This project is licensed under the MIT License.
app.py ADDED
@@ -0,0 +1,691 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import tempfile
4
+ from backend import (
5
+ extract_text_from_document,
6
+ analyze_resume_job_match,
7
+ tailor_resume,
8
+ create_word_document,
9
+ create_tailored_resume_from_template,
10
+ get_available_templates,
11
+ set_openai_api_key,
12
+ set_openai_model
13
+ )
14
+ import plotly.graph_objects as go
15
+ import time as import_time
16
+ import json
17
+
18
+ # Define sample resumes
19
+ SAMPLE_RESUMES = {
20
+ "Excellent Match Resume": "excellent_match_resume.docx",
21
+ "Good Match Resume": "good_match_resume.docx",
22
+ "Average Match Resume": "average_match_resume.docx",
23
+ "Poor Match Resume": "poor_match_resume.docx"
24
+ }
25
+
26
+ # Check if sample files exist
27
+ for sample_name, sample_path in SAMPLE_RESUMES.items():
28
+ if not os.path.exists(sample_path):
29
+ print(f"Warning: Sample resume file not found: {sample_path}")
30
+
31
+ # Read default job description
32
+ def get_default_job_description():
33
+ try:
34
+ with open("job_desc.txt", "r") as file:
35
+ return file.read()
36
+ except:
37
+ return "Software Architect position requiring cloud expertise, microservices architecture, and leadership skills."
38
+
39
+ # Function to get color based on percentage
40
+ def get_color_for_percentage(percentage):
41
+ if percentage < 40:
42
+ return "#FF4B4B" # Red for poor match
43
+ elif percentage < 60:
44
+ return "#FFA500" # Orange for average match
45
+ elif percentage < 80:
46
+ return "#2E86C1" # Blue for good match
47
+ else:
48
+ return "#2ECC71" # Green for excellent match
49
+
50
+ # Function to create match gauge chart
51
+ def create_match_gauge(match_percentage):
52
+ if match_percentage < 40:
53
+ color = "#FF4B4B" # Red
54
+ elif match_percentage < 60:
55
+ color = "#FFA500" # Orange
56
+ elif match_percentage < 80:
57
+ color = "#2E86C1" # Blue
58
+ else:
59
+ color = "#2ECC71" # Green
60
+
61
+ fig = go.Figure(go.Indicator(
62
+ mode="gauge+number",
63
+ value=match_percentage,
64
+ domain={'x': [0, 1], 'y': [0, 1]},
65
+ title={'text': "Match Percentage"},
66
+ gauge={
67
+ 'axis': {'range': [0, 100], 'tickwidth': 1, 'tickcolor': "darkblue"},
68
+ 'bar': {'color': color},
69
+ 'bgcolor': "white",
70
+ 'borderwidth': 2,
71
+ 'bordercolor': "gray",
72
+ 'steps': [
73
+ {'range': [0, 40], 'color': 'rgba(255, 75, 75, 0.2)'}, # Light red
74
+ {'range': [40, 60], 'color': 'rgba(255, 165, 0, 0.2)'}, # Light orange
75
+ {'range': [60, 80], 'color': 'rgba(46, 134, 193, 0.2)'}, # Light blue
76
+ {'range': [80, 100], 'color': 'rgba(46, 204, 113, 0.2)'} # Light green
77
+ ],
78
+ }
79
+ ))
80
+
81
+ fig.update_layout(
82
+ height=250,
83
+ margin=dict(l=20, r=20, t=50, b=20),
84
+ paper_bgcolor="rgba(0,0,0,0)",
85
+ plot_bgcolor="rgba(0,0,0,0)",
86
+ font={'color': "#444", 'family': "Arial"}
87
+ )
88
+
89
+ return fig
90
+
91
+ # Function to handle resume upload
92
+ def handle_resume_upload(file):
93
+ if file is None:
94
+ return None, None
95
+
96
+ # Save uploaded file
97
+ temp_dir = tempfile.mkdtemp()
98
+ temp_path = os.path.join(temp_dir, file.name)
99
+ with open(temp_path, "wb") as f:
100
+ f.write(file)
101
+
102
+ # Extract text
103
+ resume_text = extract_text_from_document(temp_path)
104
+ template_path = temp_path if temp_path.endswith('.docx') else None
105
+
106
+ return resume_text, template_path
107
+
108
+ # Function to handle sample resume selection
109
+ def handle_sample_resume(sample_name):
110
+ if not sample_name:
111
+ return None, None
112
+
113
+ resume_path = SAMPLE_RESUMES[sample_name]
114
+ if os.path.exists(resume_path):
115
+ # Extract text
116
+ resume_text = extract_text_from_document(resume_path)
117
+ template_path = resume_path
118
+ return resume_text, template_path
119
+ else:
120
+ print(f"Sample resume file not found: {resume_path}")
121
+ return None, None
122
+
123
+ # Function to handle template selection
124
+ def handle_template_selection(use_template, selected_template):
125
+ if use_template and selected_template != "None":
126
+ return selected_template
127
+ return None
128
+
129
+ # Function to analyze resume
130
+ def analyze_resume(resume_text, job_description, creativity_level):
131
+ if not resume_text or not job_description:
132
+ return None, "Please provide both a resume and job description."
133
+
134
+ try:
135
+ analysis_results = analyze_resume_job_match(
136
+ resume_text,
137
+ job_description,
138
+ creativity_level
139
+ )
140
+
141
+ # Create the match gauge chart
142
+ match_percentage = analysis_results.get("match_percentage", 0)
143
+ gauge_chart = create_match_gauge(match_percentage)
144
+
145
+ # Determine match category and description
146
+ if match_percentage < 40:
147
+ match_category = "Poor Match"
148
+ match_description = "Your resume needs significant improvements to match this job description."
149
+ elif match_percentage < 60:
150
+ match_category = "Average Match"
151
+ match_description = "Your resume partially matches the job description but could use improvements."
152
+ elif match_percentage < 80:
153
+ match_category = "Good Match"
154
+ match_description = "Your resume matches well with the job description with some room for improvement."
155
+ else:
156
+ match_category = "Excellent Match"
157
+ match_description = "Your resume is very well aligned with the job description!"
158
+
159
+ # Format the analysis results for display
160
+ formatted_results = f"## Match Analysis\n\n"
161
+ formatted_results += f"**Match Category:** {match_category}\n\n"
162
+ formatted_results += f"**Match Description:** {match_description}\n\n"
163
+
164
+ # Add skill breakdown
165
+ if "skill_breakdown" in analysis_results:
166
+ formatted_results += "## Skill Breakdown\n\n"
167
+ skill_breakdown = analysis_results["skill_breakdown"]
168
+
169
+ for skill_type, skill_data in skill_breakdown.items():
170
+ formatted_results += f"### {skill_type.replace('_', ' ').title()}\n"
171
+ formatted_results += f"**Percentage:** {skill_data.get('percentage', 0)}%\n"
172
+ formatted_results += f"**Comments:** {skill_data.get('comments', '')}\n\n"
173
+
174
+ # Add key matches
175
+ if "key_matches" in analysis_results:
176
+ formatted_results += "## Key Matches\n\n"
177
+ for match in analysis_results["key_matches"]:
178
+ formatted_results += f"- {match}\n"
179
+ formatted_results += "\n"
180
+
181
+ # Add gaps
182
+ if "gaps" in analysis_results:
183
+ formatted_results += "## Gaps Identified\n\n"
184
+ for gap in analysis_results["gaps"]:
185
+ formatted_results += f"- {gap}\n"
186
+ formatted_results += "\n"
187
+
188
+ # Add suggestions
189
+ if "suggestions" in analysis_results:
190
+ formatted_results += "## Improvement Suggestions\n\n"
191
+ for suggestion in analysis_results["suggestions"]:
192
+ formatted_results += f"- {suggestion}\n"
193
+ formatted_results += "\n"
194
+
195
+ # Add summary
196
+ if "summary" in analysis_results:
197
+ formatted_results += "## Summary\n\n"
198
+ formatted_results += analysis_results["summary"]
199
+
200
+ return analysis_results, formatted_results
201
+ except Exception as e:
202
+ return None, f"Error analyzing resume: {str(e)}"
203
+
204
+ # Function to tailor resume
205
+ def tailor_resume_func(resume_text, job_description, template_path, creativity_level, verbosity):
206
+ if not resume_text or not job_description:
207
+ return None, "Please provide both a resume and job description."
208
+
209
+ try:
210
+ tailored_resume = tailor_resume(
211
+ resume_text,
212
+ job_description,
213
+ template_path,
214
+ creativity_level,
215
+ verbosity
216
+ )
217
+
218
+ return tailored_resume, "Resume tailored successfully!"
219
+ except Exception as e:
220
+ return None, f"Error tailoring resume: {str(e)}"
221
+
222
+ # Function to create and download Word document
223
+ def create_word_doc(tailored_resume, template_path):
224
+ if not tailored_resume:
225
+ return None, "No tailored resume to download."
226
+
227
+ try:
228
+ # Create a temporary file
229
+ temp_dir = tempfile.mkdtemp()
230
+ output_path = os.path.join(temp_dir, "tailored_resume.docx")
231
+
232
+ # Create Word document
233
+ if template_path and os.path.exists(template_path):
234
+ success = create_tailored_resume_from_template(
235
+ tailored_resume,
236
+ template_path,
237
+ output_path
238
+ )
239
+ else:
240
+ success = create_word_document(
241
+ tailored_resume,
242
+ output_path
243
+ )
244
+
245
+ if success:
246
+ return output_path, "DOCX file created successfully!"
247
+ else:
248
+ return None, "Failed to create DOCX file."
249
+ except Exception as e:
250
+ return None, f"Error creating DOCX file: {str(e)}"
251
+
252
+ # Function to update API key
253
+ def update_api_key(api_key):
254
+ if not api_key:
255
+ return "Using API key from environment variables if available."
256
+
257
+ api_configured = set_openai_api_key(api_key)
258
+ if api_configured:
259
+ return "✅ API key configured successfully!"
260
+ else:
261
+ return "❌ Failed to configure API key."
262
+
263
+ # Function to update model
264
+ def update_model(model):
265
+ set_openai_model(model)
266
+ return f"✅ Model set to {model}"
267
+
268
+ # Main function to create the Gradio interface
269
+ def create_interface():
270
+ print("Creating interface...")
271
+ # Define the blocks with custom theme
272
+ with gr.Blocks(title="Resume Helper", theme=gr.themes.Soft(
273
+ primary_hue="blue",
274
+ secondary_hue="indigo",
275
+ font=[gr.themes.GoogleFont("Poppins"), "ui-sans-serif", "system-ui", "sans-serif"],
276
+ )) as app:
277
+ gr.Markdown("# 📄 Resume Helper")
278
+ gr.Markdown("Upload your resume and get AI-powered analysis and tailoring to match job descriptions.")
279
+
280
+ # State variables
281
+ resume_text = gr.State(None)
282
+ template_path = gr.State(None)
283
+ analysis_results = gr.State(None)
284
+ tailored_resume_text = gr.State(None)
285
+
286
+ with gr.Row():
287
+ # Left column - Inputs
288
+ with gr.Column(scale=1):
289
+ # Configuration section
290
+ with gr.Accordion("⚙️ Configuration", open=False):
291
+ api_key = gr.Textbox(
292
+ label="OpenAI API Key",
293
+ placeholder="Enter your OpenAI API key (optional)",
294
+ type="password"
295
+ )
296
+ api_status = gr.Markdown("Using API key from environment variables if available.")
297
+ api_key.change(update_api_key, inputs=[api_key], outputs=[api_status])
298
+
299
+ model_options = ["gpt-4o-mini", "gpt-4o"]
300
+ model_selector = gr.Dropdown(
301
+ label="Select AI Model",
302
+ choices=model_options,
303
+ value="gpt-4o-mini",
304
+ info="Choose the OpenAI model to use. GPT-4o-mini is faster and cheaper, while GPT-4o provides more detailed analysis."
305
+ )
306
+ model_status = gr.Markdown("")
307
+ model_selector.change(update_model, inputs=[model_selector], outputs=[model_status])
308
+
309
+ # Resume upload section
310
+ gr.Markdown("### 📤 Upload Your Resume")
311
+ resume_option = gr.Radio(
312
+ label="Choose an option:",
313
+ choices=["Upload my resume", "Use a sample resume"],
314
+ value="Upload my resume"
315
+ )
316
+
317
+ # Upload resume file
318
+ upload_file = gr.File(
319
+ label="Upload your resume (DOCX, PDF)",
320
+ file_types=[".docx", ".pdf"],
321
+ visible=True
322
+ )
323
+
324
+ # Sample resume selection
325
+ sample_resume = gr.Dropdown(
326
+ label="Select a sample resume:",
327
+ choices=list(SAMPLE_RESUMES.keys()),
328
+ visible=False
329
+ )
330
+
331
+ # Show/hide based on selection
332
+ def update_resume_option(option):
333
+ return {
334
+ upload_file: gr.update(visible=option == "Upload my resume"),
335
+ sample_resume: gr.update(visible=option == "Use a sample resume")
336
+ }
337
+
338
+ resume_option.change(update_resume_option, inputs=[resume_option], outputs=[upload_file, sample_resume])
339
+
340
+ # Template selection
341
+ gr.Markdown("### 📄 Select Resume Template (Optional)")
342
+ templates = get_available_templates()
343
+ print(f"Templates: {templates}")
344
+ use_template = gr.Checkbox(label="Use a resume template", value=False)
345
+ template_selector = gr.Dropdown(
346
+ label="Choose a template:",
347
+ choices=["None"] + templates,
348
+ value="None",
349
+ visible=False
350
+ )
351
+ print(f"Template selector: ")
352
+
353
+ def update_template_visibility(use_template):
354
+ return gr.update(visible=use_template)
355
+
356
+ use_template.change(update_template_visibility, inputs=[use_template], outputs=[template_selector])
357
+
358
+ # Job description
359
+ gr.Markdown("### 📋 Job Description")
360
+ job_description = gr.Textbox(
361
+ label="Job Description",
362
+ value=get_default_job_description(),
363
+ lines=10
364
+ )
365
+
366
+ # MOVED FROM RIGHT COLUMN: Resume detail level
367
+ gr.Markdown("### 📝 Resume Detail Level")
368
+ verbosity = gr.Radio(
369
+ label="Choose how detailed your tailored resume should be:",
370
+ choices=["Concise", "Elaborate"],
371
+ value="Elaborate"
372
+ )
373
+
374
+ # MOVED FROM RIGHT COLUMN: Creativity level
375
+ gr.Markdown("### 🎨 Creativity Level")
376
+ creativity_level = gr.Slider(
377
+ label="Adjust how creative the AI should be when tailoring your resume",
378
+ minimum=0,
379
+ maximum=100,
380
+ value=30,
381
+ step=10,
382
+ info="Higher values mean more creative modifications to your resume"
383
+ )
384
+
385
+ creativity_warning = gr.Markdown(visible=False)
386
+
387
+ def update_creativity_warning(level):
388
+ if level > 70:
389
+ return gr.update(visible=True, value="⚠️ High creativity levels may generate content that significantly modifies your original resume. Review carefully before using.")
390
+ else:
391
+ return gr.update(visible=False)
392
+
393
+ creativity_level.change(update_creativity_warning, inputs=[creativity_level], outputs=[creativity_warning])
394
+
395
+ # MOVED FROM RIGHT COLUMN: Action buttons
396
+ gr.Markdown("### 🚀 Actions")
397
+ with gr.Row():
398
+ analyze_btn = gr.Button("🔍 Analyze Resume", variant="primary")
399
+ tailor_btn = gr.Button("✏️ Tailor Resume", variant="primary")
400
+ reset_btn = gr.Button("🔄 Reset All", variant="secondary")
401
+
402
+ # Add loading indicator below the buttons
403
+ loading_indicator = gr.Markdown(visible=False)
404
+
405
+ # Right column - Results
406
+ with gr.Column(scale=1):
407
+ # Results section - Now directly in the right column, not in an accordion
408
+ with gr.Tabs() as results_tabs:
409
+ # Analysis tab
410
+ with gr.TabItem("📊 Analysis"):
411
+ analysis_plot = gr.Plot(label="Match Percentage")
412
+ analysis_output = gr.Markdown()
413
+
414
+ # Tailored Resume tab
415
+ with gr.TabItem("📝 Tailored Resume"):
416
+ tailored_resume = gr.Textbox(label="Tailored Resume", lines=15)
417
+
418
+ # Create download buttons but initially hide them
419
+ with gr.Row():
420
+ download_docx = gr.Button("📄 Download as DOCX", variant="primary", visible=False)
421
+ download_txt = gr.Button("📝 Download as TXT", variant="primary", visible=False)
422
+
423
+ # Create file components but initially hide them
424
+ docx_file = gr.File(label="Download DOCX", visible=False)
425
+ txt_file = gr.File(label="Download TXT", visible=False)
426
+
427
+ download_status = gr.Markdown()
428
+
429
+ # Event handlers
430
+ def process_resume_input(resume_opt, upload_file, sample_name, use_template_opt, template_selection):
431
+ if resume_opt == "Upload my resume" and upload_file is not None:
432
+ resume_text, template_path = handle_resume_upload(upload_file)
433
+ elif resume_opt == "Use a sample resume" and sample_name:
434
+ resume_text, template_path = handle_sample_resume(sample_name)
435
+ else:
436
+ resume_text, template_path = None, None
437
+
438
+ if use_template_opt and template_selection != "None":
439
+ template_path = template_selection
440
+
441
+ return resume_text, template_path
442
+
443
+ # Handle file upload
444
+ upload_file.upload(
445
+ lambda file: process_resume_input("Upload my resume", file, None, use_template.value, template_selector.value),
446
+ inputs=[upload_file],
447
+ outputs=[resume_text, template_path]
448
+ )
449
+
450
+ # Handle sample selection
451
+ sample_resume.change(
452
+ lambda sample: process_resume_input("Use a sample resume", None, sample, use_template.value, template_selector.value),
453
+ inputs=[sample_resume],
454
+ outputs=[resume_text, template_path]
455
+ )
456
+
457
+ # Handle template selection
458
+ template_selector.change(
459
+ lambda template, resume_txt, current_template: (resume_txt, template if template != "None" else current_template),
460
+ inputs=[template_selector, resume_text, template_path],
461
+ outputs=[resume_text, template_path]
462
+ )
463
+
464
+ # Analyze button handler with loading indicator
465
+ def analyze_with_loading(resume_txt, job_desc, creativity):
466
+ if not resume_txt:
467
+ return (
468
+ gr.update(visible=False),
469
+ None,
470
+ gr.update(visible=False),
471
+ "",
472
+ gr.update(interactive=True),
473
+ gr.update(interactive=True),
474
+ gr.update(interactive=True)
475
+ )
476
+
477
+ # Show loading message and disable buttons
478
+ yield (
479
+ gr.update(visible=True, value="⏳ Analyzing your resume... This may take a moment."),
480
+ None,
481
+ gr.update(visible=False),
482
+ "",
483
+ gr.update(interactive=False),
484
+ gr.update(interactive=False),
485
+ gr.update(interactive=False)
486
+ )
487
+
488
+ # Perform the actual analysis
489
+ results, formatted_output = analyze_resume(resume_txt, job_desc, creativity)
490
+
491
+ # Hide loading, show results, and re-enable buttons
492
+ if results:
493
+ match_percentage = results.get("match_percentage", 0)
494
+ gauge_chart = create_match_gauge(match_percentage)
495
+ yield (
496
+ gr.update(visible=False),
497
+ results,
498
+ gauge_chart,
499
+ formatted_output,
500
+ gr.update(interactive=True),
501
+ gr.update(interactive=True),
502
+ gr.update(interactive=True)
503
+ )
504
+ else:
505
+ yield (
506
+ gr.update(visible=False),
507
+ None,
508
+ gr.update(visible=False),
509
+ formatted_output,
510
+ gr.update(interactive=True),
511
+ gr.update(interactive=True),
512
+ gr.update(interactive=True)
513
+ )
514
+
515
+ analyze_btn.click(
516
+ analyze_with_loading,
517
+ inputs=[resume_text, job_description, creativity_level],
518
+ outputs=[
519
+ loading_indicator,
520
+ analysis_results,
521
+ analysis_plot,
522
+ analysis_output,
523
+ analyze_btn,
524
+ tailor_btn,
525
+ reset_btn
526
+ ],
527
+ queue=True
528
+ )
529
+
530
+ # Tailor button handler with loading indicator
531
+ def tailor_with_loading(resume_txt, job_desc, template, creativity, verbosity_level):
532
+ if not resume_txt:
533
+ return (
534
+ gr.update(visible=False),
535
+ None,
536
+ "",
537
+ gr.update(visible=False),
538
+ gr.update(visible=False),
539
+ gr.update(interactive=True),
540
+ gr.update(interactive=True),
541
+ gr.update(interactive=True)
542
+ )
543
+
544
+ # Show loading message and disable buttons
545
+ yield (
546
+ gr.update(visible=True, value="⏳ Tailoring your resume... This may take a moment."),
547
+ None,
548
+ "",
549
+ gr.update(visible=False),
550
+ gr.update(visible=False),
551
+ gr.update(interactive=False),
552
+ gr.update(interactive=False),
553
+ gr.update(interactive=False)
554
+ )
555
+
556
+ # Perform the actual tailoring
557
+ tailored, message = tailor_resume_func(
558
+ resume_txt,
559
+ job_desc,
560
+ template,
561
+ creativity,
562
+ verbosity_level.lower()
563
+ )
564
+
565
+ # Hide loading, show results, and re-enable buttons
566
+ if tailored:
567
+ # Show download buttons only when tailored resume is created
568
+ yield (
569
+ gr.update(visible=False),
570
+ tailored,
571
+ tailored,
572
+ gr.update(visible=True),
573
+ gr.update(visible=True),
574
+ gr.update(interactive=True),
575
+ gr.update(interactive=True),
576
+ gr.update(interactive=True)
577
+ )
578
+ else:
579
+ # Hide download buttons if tailoring fails
580
+ yield (
581
+ gr.update(visible=False),
582
+ None,
583
+ message,
584
+ gr.update(visible=False),
585
+ gr.update(visible=False),
586
+ gr.update(interactive=True),
587
+ gr.update(interactive=True),
588
+ gr.update(interactive=True)
589
+ )
590
+
591
+ tailor_btn.click(
592
+ tailor_with_loading,
593
+ inputs=[resume_text, job_description, template_path, creativity_level, verbosity],
594
+ outputs=[
595
+ loading_indicator,
596
+ tailored_resume_text,
597
+ tailored_resume,
598
+ download_docx,
599
+ download_txt,
600
+ analyze_btn,
601
+ tailor_btn,
602
+ reset_btn
603
+ ],
604
+ queue=True
605
+ )
606
+
607
+ # Download handlers
608
+ def create_docx_handler(tailored_txt, template):
609
+ if not tailored_txt:
610
+ return gr.update(visible=False), "No tailored resume to download."
611
+
612
+ file_path, message = create_word_doc(tailored_txt, template)
613
+ if file_path:
614
+ return gr.update(visible=True, value=file_path), message
615
+ else:
616
+ return gr.update(visible=False), message
617
+
618
+ download_docx.click(
619
+ create_docx_handler,
620
+ inputs=[tailored_resume_text, template_path],
621
+ outputs=[docx_file, download_status]
622
+ )
623
+
624
+ # Download as TXT
625
+ def download_txt_handler(tailored_txt):
626
+ if not tailored_txt:
627
+ return gr.update(visible=False), "No tailored resume to download."
628
+
629
+ # Create a temporary file
630
+ temp_dir = tempfile.mkdtemp()
631
+ output_path = os.path.join(temp_dir, "tailored_resume.txt")
632
+
633
+ with open(output_path, "w") as f:
634
+ f.write(tailored_txt)
635
+
636
+ return gr.update(visible=True, value=output_path), "TXT file created successfully!"
637
+
638
+ download_txt.click(
639
+ download_txt_handler,
640
+ inputs=[tailored_resume_text],
641
+ outputs=[txt_file, download_status]
642
+ )
643
+
644
+ # Reset handler
645
+ def reset_all():
646
+ return (
647
+ None, None, None, None,
648
+ gr.update(value=None),
649
+ gr.update(value="Upload my resume"),
650
+ gr.update(value=None),
651
+ gr.update(value=None),
652
+ gr.update(value="None"),
653
+ gr.update(value=get_default_job_description()),
654
+ gr.update(value="Elaborate"),
655
+ gr.update(value=30),
656
+ gr.update(visible=False),
657
+ gr.update(value=""),
658
+ gr.update(value=""),
659
+ gr.update(visible=False),
660
+ gr.update(visible=False),
661
+ gr.update(visible=False),
662
+ gr.update(visible=False)
663
+ )
664
+
665
+ reset_btn.click(
666
+ reset_all,
667
+ inputs=[],
668
+ outputs=[
669
+ resume_text, template_path, analysis_results, tailored_resume_text,
670
+ upload_file, resume_option, sample_resume, use_template, template_selector,
671
+ job_description, verbosity, creativity_level, creativity_warning,
672
+ analysis_output, tailored_resume,
673
+ download_docx, download_txt, docx_file, txt_file
674
+ ]
675
+ )
676
+
677
+ # Footer
678
+ gr.Markdown("---")
679
+ gr.Markdown("### 📝 Disclaimer")
680
+ gr.Markdown("""
681
+ This tool uses AI to analyze and tailor resumes. While it strives for accuracy, please review all generated content before using it professionally.
682
+ Higher creativity levels may generate content that requires more thorough verification. Always ensure that your resume accurately represents your skills and experience.
683
+ """)
684
+ print("Interface created successfully")
685
+ return app
686
+
687
+ # Launch the app
688
+ if __name__ == "__main__":
689
+ app = create_interface()
690
+ app.queue() # Enable the queue for the app
691
+ app.launch()
average_match_resume.docx ADDED
Binary file (37.8 kB). View file
 
average_match_resume.docx.txt ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ DAVID CHEN
2
+ Software Developer
3
+ [email protected] | (555) 456-7890 | linkedin.com/in/davidchen
4
+ Boston, MA | Remote Available
5
+
6
+ PROFESSIONAL SUMMARY
7
+ Software Developer with 6 years of experience building web applications and services. Experienced in full-stack development with a focus on backend systems and API development. Seeking to leverage my technical skills in a software architecture role.
8
+
9
+ SKILLS
10
+ • Programming: Java, JavaScript, TypeScript, Node.js
11
+ • Web Technologies: React, Angular, HTML, CSS
12
+ • Databases: MySQL, PostgreSQL, MongoDB
13
+ • Cloud: Basic AWS (EC2, S3)
14
+ • Tools: Git, JIRA, VS Code
15
+ • Testing: Jest, JUnit, Selenium
16
+ • Methodologies: Agile, Scrum
17
+
18
+ PROFESSIONAL EXPERIENCE
19
+
20
+ SENIOR SOFTWARE DEVELOPER | WebTech Solutions | 2019 - Present
21
+ • Develop and maintain full-stack web applications using React, Node.js, and PostgreSQL
22
+ • Design and implement RESTful APIs for internal and external service integration
23
+ • Collaborate with product managers to understand business requirements
24
+ • Participate in code reviews and provide feedback to team members
25
+ • Implement automated testing using Jest and Selenium
26
+ • Deploy applications to AWS using EC2 and S3
27
+ • Mentor junior developers on coding standards and best practices
28
+
29
+ SOFTWARE DEVELOPER | Digital Creations | 2016 - 2019
30
+ • Developed front-end interfaces using Angular and TypeScript
31
+ • Created and maintained backend services using Java Spring Boot
32
+ • Implemented database schemas and queries in MySQL
33
+ • Participated in agile development processes, including daily stand-ups and sprint planning
34
+ • Collaborated with UX designers to implement user interfaces
35
+ • Fixed bugs and improved application performance
36
+ • Documented code and created technical specifications
37
+
38
+ JUNIOR DEVELOPER | Tech Startups Inc. | 2015 - 2016
39
+ • Assisted in the development of web applications using JavaScript and PHP
40
+ • Created responsive web designs using HTML and CSS
41
+ • Fixed bugs and implemented minor features
42
+ • Participated in code reviews and team meetings
43
+ • Learned and applied new technologies and frameworks
44
+
45
+ EDUCATION
46
+ • Bachelor of Science in Computer Science, Boston University, 2015
47
+
48
+ CERTIFICATIONS
49
+ • Oracle Certified Professional, Java SE 8 Programmer
50
+ • MongoDB Certified Developer
backend.py ADDED
@@ -0,0 +1,819 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import docx2txt
3
+ import docx
4
+ from docx import Document
5
+ import openai
6
+ from dotenv import load_dotenv
7
+ import json
8
+ import tempfile
9
+ import re
10
+
11
+ # Add these imports for PDF support
12
+ import PyPDF2
13
+ import io
14
+
15
+ # Load environment variables
16
+ load_dotenv()
17
+
18
+ # Set up OpenAI API key
19
+ openai.api_key = os.getenv("OPENAI_API_KEY")
20
+
21
+ # Default model
22
+ DEFAULT_MODEL = "gpt-4o"
23
+ current_model = DEFAULT_MODEL
24
+
25
+ # Function to set OpenAI API key
26
+ def set_openai_api_key(api_key=None):
27
+ """
28
+ Set the OpenAI API key from the provided key or environment variable.
29
+
30
+ Args:
31
+ api_key: Optional API key to use. If None, will use the environment variable.
32
+
33
+ Returns:
34
+ bool: True if API key is set, False otherwise
35
+ """
36
+ if api_key:
37
+ openai.api_key = api_key
38
+ return True
39
+ else:
40
+ env_api_key = os.getenv("OPENAI_API_KEY")
41
+ if env_api_key:
42
+ openai.api_key = env_api_key
43
+ return True
44
+ return False
45
+
46
+ # Function to set OpenAI model
47
+ def set_openai_model(model_name="gpt-4o"):
48
+ """
49
+ Set the OpenAI model to use for API calls.
50
+
51
+ Args:
52
+ model_name: Name of the model to use (e.g., "gpt-4o", "gpt-4o-mini")
53
+
54
+ Returns:
55
+ str: The name of the model that was set
56
+ """
57
+ global current_model
58
+ current_model = model_name
59
+ return current_model
60
+
61
+ # Set up OpenAI API key from environment variable initially
62
+ set_openai_api_key()
63
+
64
+ def extract_text_from_document(file_path):
65
+ """
66
+ Extract text from a document file (DOCX or PDF).
67
+
68
+ Args:
69
+ file_path: Path to the document file
70
+
71
+ Returns:
72
+ str: Extracted text from the document
73
+ """
74
+ try:
75
+ # Check file extension
76
+ if file_path.lower().endswith('.docx'):
77
+ # Extract text from DOCX
78
+ return docx2txt.process(file_path)
79
+ elif file_path.lower().endswith('.pdf'):
80
+ # Extract text from PDF
81
+ text = ""
82
+ with open(file_path, 'rb') as file:
83
+ pdf_reader = PyPDF2.PdfReader(file)
84
+ for page_num in range(len(pdf_reader.pages)):
85
+ page = pdf_reader.pages[page_num]
86
+ text += page.extract_text() + "\n\n"
87
+ return text
88
+ else:
89
+ raise ValueError(f"Unsupported file format: {os.path.splitext(file_path)[1]}")
90
+ except Exception as e:
91
+ print(f"Error extracting text from document: {e}")
92
+ return None
93
+
94
+ def parse_resume(file_path):
95
+ """
96
+ Parse a resume file and extract its content.
97
+
98
+ Args:
99
+ file_path: Path to the resume file
100
+
101
+ Returns:
102
+ str: Extracted text from the resume
103
+ """
104
+ try:
105
+ return extract_text_from_document(file_path)
106
+ except Exception as e:
107
+ print(f"Error parsing resume: {e}")
108
+ return None
109
+
110
+ def analyze_resume_job_match(resume_text, job_description, creativity_level=30):
111
+ """
112
+ Analyze the match between a resume and job description using GPT-4o.
113
+
114
+ Args:
115
+ resume_text: Raw text of the resume
116
+ job_description: Job description text
117
+ creativity_level: Level of creativity/modification allowed (0-100)
118
+
119
+ Returns:
120
+ dict: Analysis results including match percentage, gaps, and suggestions
121
+ """
122
+ try:
123
+ print(f"Analyzing resume match with creativity level: {creativity_level}")
124
+ print(f"Resume text length: {len(resume_text)}")
125
+ print(f"Job description length: {len(job_description)}")
126
+
127
+ # Adjust system message based on creativity level
128
+ if creativity_level < 20:
129
+ system_message = "You are a conservative resume analyzer. Focus only on exact matches between the resume and job description. Be strict in your evaluation."
130
+ elif creativity_level < 50:
131
+ system_message = "You are a balanced resume analyzer. Evaluate the resume against the job description with a moderate level of flexibility, recognizing transferable skills."
132
+ elif creativity_level < 80:
133
+ system_message = "You are a creative resume analyzer. Be generous in your evaluation, recognizing potential and transferable skills even when not explicitly stated."
134
+ else:
135
+ system_message = "You are an optimistic resume analyzer. Focus on potential rather than exact matches. Be very generous in your evaluation and provide ambitious suggestions for improvement."
136
+
137
+ prompt = f"""
138
+ Analyze the following resume and job description:
139
+
140
+ RESUME:
141
+ {resume_text}
142
+
143
+ JOB DESCRIPTION:
144
+ {job_description}
145
+
146
+ CREATIVITY LEVEL: {creativity_level}% (where 0% means strictly factual and 100% means highly creative)
147
+
148
+ Provide a detailed analysis in JSON format with the following structure:
149
+ 1. "match_percentage": A numerical percentage (0-100) representing how well the resume matches the job description. Use job skills keyword used in job description to match with the contents of the resume to come up with the match percentage.
150
+ 2. "key_matches": List of skills and experiences in the resume that match the job requirements.
151
+ 3. "gaps": List of skills or experiences mentioned in the job description that are missing from the resume.
152
+ 4. "suggestions": Specific suggestions to improve the resume for this job. Ensure the suggestions are based on the job description and the resume and contain the exact keywords from the job description.
153
+ 5. "summary": A brief summary of the overall match and main recommendations.
154
+ 6. "skill_breakdown": An object containing categories of skills from the job description and how well the candidate matches each category:
155
+ - "technical_skills": Assessment of technical skills match (percentage and comments)
156
+ - "experience": Assessment of experience requirements match (percentage and comments)
157
+ - "education": Assessment of education requirements match (percentage and comments)
158
+ - "soft_skills": Assessment of soft skills/leadership match (percentage and comments)
159
+
160
+ Adjust your analysis based on the creativity level. Higher creativity means being more generous with matches and more ambitious with suggestions.
161
+
162
+ IMPORTANT: For each resume, provide a unique and accurate match percentage based on the actual content. Do not use the same percentage for different resumes. The excellent match resume should have a high percentage (80-95%), good match should be moderate-high (65-80%), average match should be moderate (40-65%), and poor match should be low (below 40%).
163
+
164
+ Return ONLY the JSON object without any additional text.
165
+ """
166
+
167
+ response = openai.chat.completions.create(
168
+ model=current_model,
169
+ messages=[
170
+ {"role": "system", "content": system_message},
171
+ {"role": "user", "content": prompt}
172
+ ],
173
+ response_format={"type": "json_object"}
174
+ )
175
+
176
+ analysis = json.loads(response.choices[0].message.content)
177
+ print(f"Analysis complete. Match percentage: {analysis.get('match_percentage', 0)}%")
178
+ return analysis
179
+
180
+ except Exception as e:
181
+ print(f"Error analyzing resume: {e}")
182
+ return {
183
+ "match_percentage": 0,
184
+ "key_matches": [],
185
+ "gaps": ["Error analyzing resume"],
186
+ "suggestions": ["Please try again"],
187
+ "summary": f"Error: {str(e)}",
188
+ "skill_breakdown": {
189
+ "technical_skills": {"percentage": 0, "comments": "Error analyzing resume"},
190
+ "experience": {"percentage": 0, "comments": "Error analyzing resume"},
191
+ "education": {"percentage": 0, "comments": "Error analyzing resume"},
192
+ "soft_skills": {"percentage": 0, "comments": "Error analyzing resume"}
193
+ }
194
+ }
195
+
196
+ def tailor_resume(resume_text, job_description, template_path=None, creativity_level=30, verbosity="elaborate"):
197
+ """
198
+ Generate a tailored resume based on the job description using GPT-4o.
199
+
200
+ Args:
201
+ resume_text: Raw text of the original resume
202
+ job_description: Job description text
203
+ template_path: Optional path to a resume template
204
+ creativity_level: Level of creativity/modification allowed (0-100)
205
+ verbosity: Level of detail in the resume ('concise' or 'elaborate')
206
+
207
+ Returns:
208
+ str: Tailored resume content
209
+ """
210
+ try:
211
+ # If a template is provided, read its structure
212
+ template_structure = ""
213
+ template_sections = []
214
+
215
+ if template_path:
216
+ # Extract the template structure and sections
217
+ doc = Document(template_path)
218
+ for para in doc.paragraphs:
219
+ if para.text.strip():
220
+ template_structure += para.text + "\n"
221
+ # Identify section headings (usually in all caps or with specific styles)
222
+ if para.style.name.startswith('Heading') or para.text.isupper() or (para.runs and para.runs[0].bold):
223
+ template_sections.append(para.text.strip())
224
+
225
+ # Adjust system message based on creativity level
226
+ if creativity_level < 20:
227
+ system_message = "You are a conservative resume editor. Only reorganize existing content to better match the job description. Do not add any new experiences or skills that aren't explicitly mentioned in the original resume."
228
+ elif creativity_level < 50:
229
+ system_message = "You are a balanced resume editor. Enhance existing content with better wording and highlight relevant skills. Make minor improvements but keep all content factual and based on the original resume."
230
+ elif creativity_level < 80:
231
+ system_message = "You are a creative resume editor. Significantly enhance the resume with improved wording and may suggest minor additions or extensions of existing experiences to better match the job description."
232
+ else:
233
+ system_message = "You are an aggressive resume optimizer. Optimize the resume to perfectly match the job description, including suggesting new skills and experiences that would make the candidate more competitive, while maintaining some connection to their actual background. Ensure exact keywords are included from the job description in the new resume. "
234
+
235
+ # Adjust system message based on creativity level and verbosity
236
+ if creativity_level < 20:
237
+ base_message = "You are a conservative resume editor. Only reorganize existing content to better match the job description. Do not add any new experiences or skills that aren't explicitly mentioned in the original resume."
238
+ elif creativity_level < 50:
239
+ base_message = "You are a balanced resume editor. Enhance existing content with better wording and highlight relevant skills. Make minor improvements but keep all content factual and based on the original resume."
240
+ elif creativity_level < 80:
241
+ base_message = "You are a creative resume editor. Significantly enhance the resume with improved wording and may suggest minor additions or extensions of existing experiences to better match the job description."
242
+ else:
243
+ base_message = "You are an aggressive resume optimizer. Optimize the resume to perfectly match the job description, including suggesting new skills and experiences that would make the candidate more competitive, while maintaining some connection to their actual background. Ensure exact keywords are included from the job description in the new resume."
244
+
245
+ # Add verbosity instructions to the system message
246
+ if verbosity == "concise":
247
+ system_message = base_message + " Create a concise resume with brief bullet points, focusing only on the most relevant information. Aim for a shorter resume that can be quickly scanned by recruiters."
248
+ else: # elaborate
249
+ system_message = base_message + " Create a detailed resume that thoroughly explains experiences and skills, providing context and specific achievements. Use comprehensive bullet points to showcase the candidate's qualifications."
250
+
251
+ prompt = f"""
252
+ Create a tailored version of this resume to better match the job description:
253
+
254
+ ORIGINAL RESUME:
255
+ {resume_text}
256
+
257
+ JOB DESCRIPTION:
258
+ {job_description}
259
+
260
+ {("TEMPLATE STRUCTURE TO FOLLOW:" + chr(10) + template_structure) if template_path else ""}
261
+
262
+ {("TEMPLATE SECTIONS TO INCLUDE:" + chr(10) + chr(10).join(template_sections)) if template_sections else ""}
263
+
264
+ CREATIVITY LEVEL: {creativity_level}% (where 0% means strictly factual and 100% means highly creative)
265
+ VERBOSITY: {verbosity.upper()} (CONCISE means brief and to-the-point, ELABORATE means detailed and comprehensive)
266
+
267
+ Create a tailored resume that:
268
+ 1. Highlights relevant skills and experiences that match the job description
269
+ 2. Uses keywords from the job description
270
+ 3. Quantifies achievements where possible
271
+ 4. Removes or downplays irrelevant information
272
+ 5. Adjusts content based on the specified creativity level
273
+
274
+ IMPORTANT FORMATTING INSTRUCTIONS:
275
+ - Format your response with clear section headings in ALL CAPS
276
+ - Use bullet points (•) for listing items and achievements
277
+ - If a template is provided, follow its exact section structure and organization
278
+ - Maintain the same section headings as in the template when possible
279
+ - For each section, provide content that matches the requested verbosity level:
280
+ * CONCISE: Use 1-2 line bullet points, focus only on the most relevant achievements
281
+ * ELABORATE: Use detailed bullet points with context and specific metrics
282
+
283
+ Return the complete tailored resume content in a professional format.
284
+ """
285
+
286
+ response = openai.chat.completions.create(
287
+ model=current_model,
288
+ messages=[
289
+ {"role": "system", "content": system_message},
290
+ {"role": "user", "content": prompt}
291
+ ]
292
+ )
293
+
294
+ tailored_resume = response.choices[0].message.content
295
+ return tailored_resume
296
+
297
+ except Exception as e:
298
+ print(f"Error tailoring resume: {e}")
299
+ return f"Error tailoring resume: {str(e)}"
300
+
301
+ def create_word_document(content, output_path, template_path=None):
302
+ """
303
+ Create a Word document with the given content, optionally using a template.
304
+
305
+ Args:
306
+ content: Text content for the document
307
+ output_path: Path to save the document
308
+ template_path: Optional path to a template document to use as a base
309
+
310
+ Returns:
311
+ bool: Success status
312
+ """
313
+ try:
314
+ # If a template is provided, use it as the base document
315
+ if template_path and os.path.exists(template_path):
316
+ # Create a new document instead of modifying the template directly
317
+ doc = Document()
318
+ original_template = Document(template_path)
319
+
320
+ # Copy all styles from the template to the new document
321
+ for style in original_template.styles:
322
+ if style.name not in doc.styles:
323
+ try:
324
+ doc.styles.add_style(style.name, style.type)
325
+ except:
326
+ pass # Style might already exist or be built-in
327
+
328
+ # Copy document properties and sections settings
329
+ # We'll only add sections as needed, not at the beginning
330
+ if len(original_template.sections) > 0:
331
+ # Copy properties from the first section
332
+ section = original_template.sections[0]
333
+ # Use the existing first section in the new document
334
+ new_section = doc.sections[0]
335
+ new_section.page_height = section.page_height
336
+ new_section.page_width = section.page_width
337
+ new_section.left_margin = section.left_margin
338
+ new_section.right_margin = section.right_margin
339
+ new_section.top_margin = section.top_margin
340
+ new_section.bottom_margin = section.bottom_margin
341
+ new_section.header_distance = section.header_distance
342
+ new_section.footer_distance = section.footer_distance
343
+
344
+ # Copy additional sections if needed
345
+ for i in range(1, len(original_template.sections)):
346
+ section = original_template.sections[i]
347
+ new_section = doc.add_section()
348
+ new_section.page_height = section.page_height
349
+ new_section.page_width = section.page_width
350
+ new_section.left_margin = section.left_margin
351
+ new_section.right_margin = section.right_margin
352
+ new_section.top_margin = section.top_margin
353
+ new_section.bottom_margin = section.bottom_margin
354
+ new_section.header_distance = section.header_distance
355
+ new_section.footer_distance = section.footer_distance
356
+
357
+ # Copy complex elements like headers and footers
358
+ copy_template_complex_elements(original_template, doc)
359
+
360
+ # Split content by sections (using headings as delimiters)
361
+ sections = []
362
+ current_section = []
363
+ lines = content.split('\n')
364
+
365
+ for line in lines:
366
+ line = line.strip()
367
+ if not line:
368
+ continue
369
+
370
+ # Check if this is a heading (all caps or ends with a colon)
371
+ if line.isupper() or (line.endswith(':') and len(line) < 50):
372
+ if current_section:
373
+ sections.append(current_section)
374
+ current_section = [line]
375
+ else:
376
+ current_section.append(line)
377
+
378
+ if current_section:
379
+ sections.append(current_section)
380
+
381
+ # Find template headings to match with our content sections
382
+ template_headings = []
383
+ template_heading_styles = {}
384
+ for para in original_template.paragraphs:
385
+ if para.style.name.startswith('Heading') or para.text.isupper() or (para.runs and para.runs[0].bold):
386
+ template_headings.append(para.text.strip())
387
+ template_heading_styles[para.text.strip()] = para.style.name
388
+
389
+ # Add content to the document with appropriate formatting
390
+ for section in sections:
391
+ if not section:
392
+ continue
393
+
394
+ # First line of each section is treated as a heading
395
+ heading = section[0]
396
+
397
+ # Try to find a matching heading style from the template
398
+ heading_style = 'Heading 1' # Default
399
+ for template_heading in template_headings:
400
+ if template_heading.upper() == heading.upper() or template_heading.upper() in heading.upper() or heading.upper() in template_heading.upper():
401
+ heading_style = template_heading_styles.get(template_heading, 'Heading 1')
402
+ break
403
+
404
+ # Add the heading with the appropriate style
405
+ p = doc.add_paragraph()
406
+ try:
407
+ p.style = heading_style
408
+ except:
409
+ p.style = 'Heading 1' # Fallback
410
+
411
+ run = p.add_run(heading)
412
+ run.bold = True
413
+
414
+ # Add the rest of the section content
415
+ for line in section[1:]:
416
+ if line.startswith('•') or line.startswith('-') or line.startswith('*'):
417
+ # This is a bullet point
418
+ p = doc.add_paragraph(line[1:].strip(), style='List Bullet')
419
+ else:
420
+ p = doc.add_paragraph(line)
421
+ else:
422
+ # Create a new document with basic formatting
423
+ doc = Document()
424
+
425
+ # Split content by lines and add to document with basic formatting
426
+ paragraphs = content.split('\n')
427
+ for para in paragraphs:
428
+ para = para.strip()
429
+ if not para:
430
+ continue
431
+
432
+ # Check if this is a heading (all caps or ends with a colon)
433
+ if para.isupper() or (para.endswith(':') and len(para) < 50):
434
+ p = doc.add_paragraph()
435
+ p.style = 'Heading 1'
436
+ run = p.add_run(para)
437
+ run.bold = True
438
+ elif para.startswith('•') or para.startswith('-') or para.startswith('*'):
439
+ # This is a bullet point
440
+ p = doc.add_paragraph(para[1:].strip(), style='List Bullet')
441
+ else:
442
+ doc.add_paragraph(para)
443
+
444
+ doc.save(output_path)
445
+ return True
446
+
447
+ except Exception as e:
448
+ print(f"Error creating Word document: {e}")
449
+ return False
450
+
451
+ def get_available_templates():
452
+ """
453
+ Get a list of available resume templates from the current working directory.
454
+
455
+ Returns:
456
+ list: List of template file paths
457
+ """
458
+ # First, copy any templates from templates directory if they don't exist
459
+ #copy_templates_to_current_directory()
460
+
461
+ templates = []
462
+
463
+ # Check current directory for templates
464
+ for file in os.listdir("."):
465
+ if file.endswith("_Template.docx") or file.endswith("Template.docx"):
466
+ templates.append(file)
467
+
468
+ print(f"Found templates in current directory: {templates}")
469
+ return templates
470
+
471
+ def copy_templates_to_current_directory():
472
+ """
473
+ Copy templates from the templates directory to the current working directory
474
+ if they don't already exist.
475
+
476
+ Returns:
477
+ list: List of copied template file paths
478
+ """
479
+ copied_templates = []
480
+ templates_dir = "templates"
481
+
482
+ # Check if templates directory exists
483
+ if not os.path.exists(templates_dir):
484
+ print(f"Templates directory '{templates_dir}' not found.")
485
+ return copied_templates
486
+
487
+ # Get list of template files in templates directory
488
+ template_files = [f for f in os.listdir(templates_dir) if f.endswith(".docx")]
489
+
490
+ # Copy each template file to current directory if it doesn't exist
491
+ for template_file in template_files:
492
+ source_path = os.path.join(templates_dir, template_file)
493
+ dest_path = template_file
494
+
495
+ if not os.path.exists(dest_path):
496
+ try:
497
+ import shutil
498
+ shutil.copy2(source_path, dest_path)
499
+ copied_templates.append(dest_path)
500
+ print(f"Copied template '{template_file}' to current directory.")
501
+ except Exception as e:
502
+ print(f"Error copying template '{template_file}': {e}")
503
+ else:
504
+ print(f"Template '{template_file}' already exists in current directory.")
505
+
506
+ return copied_templates
507
+
508
+ def copy_template_complex_elements(source_doc, target_doc):
509
+ """
510
+ Copy complex elements like headers and footers from source document to target document.
511
+
512
+ Args:
513
+ source_doc: Source Document object
514
+ target_doc: Target Document object
515
+ """
516
+ try:
517
+ # Copy headers and footers
518
+ for i, section in enumerate(target_doc.sections):
519
+ # Skip if source doesn't have this many sections
520
+ if i >= len(source_doc.sections):
521
+ break
522
+
523
+ # Copy header
524
+ if section.header.is_linked_to_previous == False:
525
+ # Check if there's at least one paragraph in the header
526
+ if len(section.header.paragraphs) == 0:
527
+ section.header.add_paragraph()
528
+
529
+ # Copy text and style from source header paragraphs
530
+ for j, para in enumerate(source_doc.sections[i].header.paragraphs):
531
+ if j < len(section.header.paragraphs):
532
+ section.header.paragraphs[j].text = para.text
533
+ try:
534
+ section.header.paragraphs[j].style = para.style
535
+ except Exception:
536
+ pass # Style might not be compatible
537
+ else:
538
+ new_para = section.header.add_paragraph(para.text)
539
+ try:
540
+ new_para.style = para.style
541
+ except Exception:
542
+ pass
543
+
544
+ # Copy footer
545
+ if section.footer.is_linked_to_previous == False:
546
+ # Check if there's at least one paragraph in the footer
547
+ if len(section.footer.paragraphs) == 0:
548
+ section.footer.add_paragraph()
549
+
550
+ # Copy text and style from source footer paragraphs
551
+ for j, para in enumerate(source_doc.sections[i].footer.paragraphs):
552
+ if j < len(section.footer.paragraphs):
553
+ section.footer.paragraphs[j].text = para.text
554
+ try:
555
+ section.footer.paragraphs[j].style = para.style
556
+ except Exception:
557
+ pass # Style might not be compatible
558
+ else:
559
+ new_para = section.footer.add_paragraph(para.text)
560
+ try:
561
+ new_para.style = para.style
562
+ except Exception:
563
+ pass
564
+ except Exception as e:
565
+ print(f"Error copying complex elements: {e}")
566
+
567
+ def create_tailored_resume_from_template(content, template_path, output_path):
568
+ """
569
+ Create a tailored resume by directly modifying a template document.
570
+ This preserves all formatting, tables, and styles from the original template.
571
+
572
+ Args:
573
+ content: Structured content for the resume (text)
574
+ template_path: Path to the template document
575
+ output_path: Path to save the output document
576
+
577
+ Returns:
578
+ bool: Success status
579
+ """
580
+ try:
581
+ if not os.path.exists(template_path):
582
+ return False
583
+
584
+ # Create a copy of the template
585
+ doc = Document(template_path)
586
+
587
+ # Parse the content into sections
588
+ sections = {}
589
+ current_section = None
590
+ current_content = []
591
+
592
+ for line in content.split('\n'):
593
+ line = line.strip()
594
+ if not line:
595
+ continue
596
+
597
+ # Check if this is a heading (all caps or ends with a colon)
598
+ if line.isupper() or (line.endswith(':') and len(line) < 50):
599
+ # Save the previous section
600
+ if current_section and current_content:
601
+ sections[current_section] = current_content
602
+
603
+ # Start a new section
604
+ current_section = line
605
+ current_content = []
606
+ else:
607
+ if current_section:
608
+ current_content.append(line)
609
+
610
+ # Save the last section
611
+ if current_section and current_content:
612
+ sections[current_section] = current_content
613
+
614
+ # Find all paragraphs in the template that are headings or potential section markers
615
+ template_sections = {}
616
+ for i, para in enumerate(doc.paragraphs):
617
+ if para.style.name.startswith('Heading') or para.text.isupper() or para.runs and para.runs[0].bold:
618
+ template_sections[para.text.strip()] = i
619
+
620
+ # Create a new document to avoid duplicate content
621
+ new_doc = Document()
622
+
623
+ # Copy all styles from the template to the new document
624
+ for style in doc.styles:
625
+ if style.name not in new_doc.styles:
626
+ try:
627
+ new_doc.styles.add_style(style.name, style.type)
628
+ except:
629
+ pass # Style might already exist or be built-in
630
+
631
+ # Copy document properties and sections settings
632
+ # We'll only add sections as needed, not at the beginning
633
+ if len(doc.sections) > 0:
634
+ # Copy properties from the first section
635
+ section = doc.sections[0]
636
+ # Use the existing first section in the new document
637
+ new_section = new_doc.sections[0]
638
+ new_section.page_height = section.page_height
639
+ new_section.page_width = section.page_width
640
+ new_section.left_margin = section.left_margin
641
+ new_section.right_margin = section.right_margin
642
+ new_section.top_margin = section.top_margin
643
+ new_section.bottom_margin = section.bottom_margin
644
+ new_section.header_distance = section.header_distance
645
+ new_section.footer_distance = section.footer_distance
646
+
647
+ # Copy additional sections if needed
648
+ for i in range(1, len(doc.sections)):
649
+ section = doc.sections[i]
650
+ new_section = new_doc.add_section()
651
+ new_section.page_height = section.page_height
652
+ new_section.page_width = section.page_width
653
+ new_section.left_margin = section.left_margin
654
+ new_section.right_margin = section.right_margin
655
+ new_section.top_margin = section.top_margin
656
+ new_section.bottom_margin = section.bottom_margin
657
+ new_section.header_distance = section.header_distance
658
+ new_section.footer_distance = section.footer_distance
659
+
660
+ # Copy complex elements like headers and footers
661
+ copy_template_complex_elements(doc, new_doc)
662
+
663
+ # Replace content in the template with our tailored content
664
+ # First, create a mapping between our sections and template sections
665
+ section_mapping = {}
666
+ for our_section in sections.keys():
667
+ best_match = None
668
+ best_score = 0
669
+
670
+ for template_section in template_sections.keys():
671
+ # Calculate similarity between section headings
672
+ if template_section.upper() == our_section.upper():
673
+ # Exact match
674
+ best_match = template_section
675
+ break
676
+ elif template_section.upper() in our_section.upper() or our_section.upper() in template_section.upper():
677
+ # Partial match
678
+ score = len(set(template_section.upper()) & set(our_section.upper())) / max(len(template_section), len(our_section))
679
+ if score > best_score:
680
+ best_score = score
681
+ best_match = template_section
682
+
683
+ if best_match and best_score > 0.5:
684
+ section_mapping[our_section] = best_match
685
+
686
+ # Add content to the new document based on the template structure
687
+ for our_section, content_lines in sections.items():
688
+ # Add section heading
689
+ p = new_doc.add_paragraph()
690
+
691
+ # Try to find a matching heading style from the template
692
+ if our_section in section_mapping:
693
+ template_section = section_mapping[our_section]
694
+ section_index = template_sections[template_section]
695
+ template_para = doc.paragraphs[section_index]
696
+
697
+ try:
698
+ p.style = template_para.style.name
699
+ except:
700
+ p.style = 'Heading 1' # Fallback
701
+
702
+ # Copy formatting from template paragraph
703
+ for run in template_para.runs:
704
+ if run.text.strip():
705
+ p_run = p.add_run(our_section)
706
+ p_run.bold = run.bold
707
+ p_run.italic = run.italic
708
+ p_run.underline = run.underline
709
+ if run.font.name:
710
+ p_run.font.name = run.font.name
711
+ if run.font.size:
712
+ p_run.font.size = run.font.size
713
+ if run.font.color.rgb:
714
+ p_run.font.color.rgb = run.font.color.rgb
715
+ break
716
+ else:
717
+ # If no runs with text, add a default run
718
+ p_run = p.add_run(our_section)
719
+ p_run.bold = True
720
+ else:
721
+ # No matching template section, use default formatting
722
+ p.style = 'Heading 1'
723
+ p_run = p.add_run(our_section)
724
+ p_run.bold = True
725
+
726
+ # Add section content
727
+ for line in content_lines:
728
+ if line.startswith('•') or line.startswith('-') or line.startswith('*'):
729
+ # This is a bullet point
730
+ p = new_doc.add_paragraph(line[1:].strip(), style='List Bullet')
731
+ else:
732
+ p = new_doc.add_paragraph(line)
733
+
734
+ new_doc.save(output_path)
735
+ return True
736
+
737
+ except Exception as e:
738
+ print(f"Error creating tailored resume from template: {e}")
739
+ return False
740
+
741
+ def convert_text_to_word(text_file_path, output_docx_path):
742
+ """
743
+ Convert a text file to a Word document.
744
+
745
+ Args:
746
+ text_file_path: Path to the text file
747
+ output_docx_path: Path where the Word document will be saved
748
+
749
+ Returns:
750
+ bool: True if successful, False otherwise
751
+ """
752
+ try:
753
+ # Read the text file
754
+ with open(text_file_path, 'r', encoding='utf-8') as file:
755
+ content = file.read()
756
+
757
+ # Create a new Word document
758
+ doc = Document()
759
+
760
+ # Split the content by double newlines to identify paragraphs
761
+ paragraphs = content.split('\n\n')
762
+
763
+ # Process each paragraph
764
+ for i, para_text in enumerate(paragraphs):
765
+ # Skip empty paragraphs
766
+ if not para_text.strip():
767
+ continue
768
+
769
+ # Check if this looks like a heading (all caps or ends with a colon)
770
+ is_heading = para_text.isupper() or para_text.strip().endswith(':')
771
+
772
+ # Add the paragraph to the document
773
+ paragraph = doc.add_paragraph(para_text.strip())
774
+
775
+ # Apply formatting based on position and content
776
+ if i == 0: # First paragraph is likely the name
777
+ paragraph.style = 'Title'
778
+ elif is_heading:
779
+ paragraph.style = 'Heading 2'
780
+ else:
781
+ paragraph.style = 'Normal'
782
+
783
+ # Save the document
784
+ doc.save(output_docx_path)
785
+ return True
786
+
787
+ except Exception as e:
788
+ print(f"Error converting text to Word: {e}")
789
+ return False
790
+
791
+ def convert_sample_resumes():
792
+ """
793
+ Convert all sample text resumes to Word documents.
794
+
795
+ Returns:
796
+ list: Paths to the created Word documents
797
+ """
798
+ sample_files = [
799
+ "excellent_match_resume.docx.txt",
800
+ "good_match_resume.docx.txt",
801
+ "average_match_resume.docx.txt",
802
+ "poor_match_resume.docx.txt"
803
+ ]
804
+
805
+ created_files = []
806
+
807
+ for text_file in sample_files:
808
+ if os.path.exists(text_file):
809
+ output_path = text_file.replace('.docx.txt', '.docx')
810
+ print(f"Converting {text_file} to {output_path}...")
811
+ if convert_text_to_word(text_file, output_path):
812
+ created_files.append(output_path)
813
+ print(f"Successfully created {output_path}")
814
+ else:
815
+ print(f"Failed to create {output_path}")
816
+ else:
817
+ print(f"Sample file not found: {text_file}")
818
+
819
+ return created_files
create_templates.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ # Add the templates directory to the Python path
5
+ templates_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
6
+ sys.path.append(templates_dir)
7
+
8
+ # Import the template creation functions
9
+ from create_templates import (
10
+ create_professional_template,
11
+ create_modern_template,
12
+ create_academic_template
13
+ )
14
+
15
+ def main():
16
+ """Create resume templates in the current working directory."""
17
+ print("Creating resume templates in the current directory...")
18
+
19
+ # Create templates
20
+ create_professional_template()
21
+ create_modern_template()
22
+ create_academic_template()
23
+
24
+ print("All templates created successfully in the current directory!")
25
+
26
+ # List created templates
27
+ templates = [f for f in os.listdir(".") if f.endswith("_Template.docx")]
28
+ print(f"Templates available: {templates}")
29
+
30
+ if __name__ == "__main__":
31
+ main()
excellent_match_resume.docx ADDED
Binary file (38.3 kB). View file
 
excellent_match_resume.docx.txt ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MICHAEL JOHNSON
2
+ Senior Software Architect
3
+ [email protected] | (555) 123-4567 | linkedin.com/in/michaeljohnson
4
+ San Francisco, CA | Remote Available
5
+
6
+ PROFESSIONAL SUMMARY
7
+ Innovative Software Architect with 12+ years of experience designing and implementing scalable, high-performance enterprise applications. Expert in cloud architecture, microservices, and distributed systems with a proven track record of delivering complex solutions. Skilled in translating business requirements into robust technical designs while mentoring development teams and promoting best practices.
8
+
9
+ SKILLS
10
+ • Cloud Platforms: AWS (Certified Solutions Architect), Azure, GCP
11
+ • Architecture: Microservices, Event-driven, Serverless, API Design (REST, GraphQL)
12
+ • Containerization: Docker, Kubernetes, ECS
13
+ • Programming: Java, Python, TypeScript, C#
14
+ • DevOps: CI/CD, Jenkins, GitHub Actions, ArgoCD
15
+ • Infrastructure as Code: Terraform, CloudFormation
16
+ • Databases: PostgreSQL, MongoDB, DynamoDB, Cassandra
17
+ • Messaging: Kafka, RabbitMQ, AWS Kinesis
18
+ • Security: OAuth, JWT, API Gateway
19
+
20
+ PROFESSIONAL EXPERIENCE
21
+
22
+ SENIOR SOFTWARE ARCHITECT | Cloudwave Technologies | 2019 - Present
23
+ • Led the architectural redesign of a monolithic application into a microservices architecture, resulting in 40% improved scalability and 30% reduced deployment time
24
+ • Designed and implemented cloud-native solutions on AWS using containerization (Docker, Kubernetes) and serverless technologies (Lambda, API Gateway)
25
+ • Created comprehensive architecture documentation and technical specifications for enterprise applications
26
+ • Established CI/CD pipelines using Jenkins and GitHub Actions, reducing deployment errors by 75%
27
+ • Mentored a team of 15 developers on best practices in software architecture and cloud technologies
28
+ • Implemented event-driven architecture using Kafka for real-time data processing, handling 10M+ daily events
29
+ • Designed and optimized database schemas across SQL and NoSQL databases for high-performance applications
30
+
31
+ LEAD SOFTWARE ENGINEER | TechInnovate Solutions | 2015 - 2019
32
+ • Architected and developed a distributed system handling 5,000+ concurrent users with 99.99% uptime
33
+ • Led the migration of on-premises applications to AWS, reducing infrastructure costs by 35%
34
+ • Designed RESTful and GraphQL APIs for internal and external consumption
35
+ • Implemented microservices using Spring Boot and Docker, improving system modularity and maintainability
36
+ • Created architecture blueprints and technical documentation for development teams
37
+ • Collaborated with product managers to translate business requirements into technical solutions
38
+ • Introduced automated testing frameworks, increasing code coverage from 60% to 90%
39
+
40
+ SOFTWARE ENGINEER | Global Systems Inc. | 2011 - 2015
41
+ • Developed enterprise Java applications using Spring Framework
42
+ • Designed and implemented database schemas and stored procedures in PostgreSQL
43
+ • Created front-end interfaces using Angular and React
44
+ • Participated in agile development processes, including daily stand-ups and sprint planning
45
+ • Contributed to architectural decisions for new product features
46
+
47
+ EDUCATION
48
+ • Master of Science in Computer Science, Stanford University, 2011
49
+ • Bachelor of Science in Software Engineering, University of California, Berkeley, 2009
50
+
51
+ CERTIFICATIONS
52
+ • AWS Certified Solutions Architect - Professional
53
+ • Microsoft Azure Solutions Architect Expert
54
+ • Certified Kubernetes Administrator (CKA)
55
+ • Professional Scrum Master I (PSM I)
find_error.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ def find_problematic_fstrings(file_path):
4
+ with open(file_path, 'r', encoding='utf-8') as file:
5
+ lines = file.readlines()
6
+
7
+ for i, line in enumerate(lines):
8
+ # Look for f-strings with backslashes
9
+ if 'f"' in line or "f'" in line:
10
+ # Check if there's a backslash in the f-string
11
+ match = re.search(r'f["\'].*?\\.*?["\']', line)
12
+ if match:
13
+ print(f"Potential problematic f-string at line {i+1}: {line.strip()}")
14
+
15
+ if __name__ == "__main__":
16
+ find_problematic_fstrings("backend.py")
good_match_resume.docx ADDED
Binary file (38 kB). View file
 
good_match_resume.docx.txt ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SARAH WILLIAMS
2
+ Software Architect
3
+ [email protected] | (555) 987-6543 | linkedin.com/in/sarahwilliams
4
+ Chicago, IL | Remote Available
5
+
6
+ PROFESSIONAL SUMMARY
7
+ Dedicated Software Architect with 9 years of experience in designing and implementing software solutions for enterprise applications. Skilled in cloud architecture and microservices with a focus on scalable and maintainable systems. Strong communicator with experience guiding development teams and collaborating with stakeholders.
8
+
9
+ SKILLS
10
+ • Cloud Platforms: AWS, Azure
11
+ • Architecture: Microservices, Service-oriented Architecture
12
+ • Containerization: Docker, Kubernetes
13
+ • Programming: Java, Python, JavaScript
14
+ • DevOps: CI/CD, Jenkins
15
+ • Databases: MySQL, MongoDB
16
+ • API Design: REST
17
+ • Tools: Git, JIRA, Confluence
18
+
19
+ PROFESSIONAL EXPERIENCE
20
+
21
+ SOFTWARE ARCHITECT | Enterprise Solutions Inc. | 2018 - Present
22
+ • Design and develop software architecture for enterprise applications serving 1M+ users
23
+ • Create detailed architecture specifications and documentation for development teams
24
+ • Lead the migration from monolithic to microservices architecture, improving system scalability
25
+ • Implement containerization using Docker and Kubernetes for consistent deployment
26
+ • Collaborate with product managers to translate business requirements into technical solutions
27
+ • Guide a team of 8 developers in implementing architectural designs
28
+ • Evaluate and select appropriate technologies and frameworks for new projects
29
+ • Ensure system scalability, reliability, and security through proper architectural patterns
30
+
31
+ SENIOR SOFTWARE DEVELOPER | TechSolutions Corp | 2015 - 2018
32
+ • Developed and maintained enterprise Java applications using Spring Framework
33
+ • Participated in architectural decisions for new features and system improvements
34
+ • Implemented RESTful APIs for internal and external service integration
35
+ • Designed database schemas and optimized queries for performance
36
+ • Collaborated with QA team to ensure code quality and test coverage
37
+ • Mentored junior developers on coding standards and best practices
38
+ • Participated in agile development processes, including sprint planning and retrospectives
39
+
40
+ SOFTWARE DEVELOPER | Digital Innovations | 2013 - 2015
41
+ • Developed web applications using Java, JavaScript, and HTML/CSS
42
+ • Created and maintained database schemas in MySQL
43
+ • Implemented unit and integration tests to ensure code quality
44
+ • Participated in code reviews and provided feedback to team members
45
+ • Collaborated with UX designers to implement user interfaces
46
+
47
+ EDUCATION
48
+ • Bachelor of Science in Computer Science, University of Illinois, 2013
49
+
50
+ CERTIFICATIONS
51
+ • AWS Certified Solutions Architect - Associate
52
+ • Certified Scrum Developer
job_desc.txt ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Software Architect
2
+ Location: Remote (US)
3
+ Salary: $150,000 - $180,000
4
+
5
+ About the Role:
6
+ We are seeking an experienced Software Architect to design and oversee the implementation of high-performance, scalable software systems. The ideal candidate will have a strong background in cloud architecture, microservices, and distributed systems, with a proven track record of delivering complex software solutions.
7
+
8
+ Key Responsibilities:
9
+ • Design and develop software architecture for enterprise applications
10
+ • Create detailed architecture specifications and documentation
11
+ • Collaborate with product managers to translate business requirements into technical solutions
12
+ • Guide development teams in implementing architectural designs
13
+ • Evaluate and select appropriate technologies and frameworks
14
+ • Ensure system scalability, reliability, and security
15
+ • Mentor junior developers and promote best practices
16
+ • Stay current with emerging technologies and industry trends
17
+
18
+ Required Skills and Experience:
19
+ • 8+ years of software development experience
20
+ • 5+ years in software architecture roles
21
+ • Expert knowledge of cloud platforms (AWS, Azure, or GCP)
22
+ • Strong experience with microservices architecture
23
+ • Proficiency in at least one major programming language (Java, Python, C#, or JavaScript/TypeScript)
24
+ • Experience with containerization (Docker, Kubernetes)
25
+ • Knowledge of CI/CD pipelines and DevOps practices
26
+ • Understanding of database design (SQL and NoSQL)
27
+ • Experience with API design and implementation (REST, GraphQL)
28
+ • Strong communication and leadership skills
29
+
30
+ Preferred Qualifications:
31
+ • Experience with event-driven architecture
32
+ • Knowledge of serverless computing
33
+ • Experience with data streaming platforms (Kafka, Kinesis)
34
+ • Understanding of machine learning frameworks
35
+ • Experience with infrastructure as code (Terraform, CloudFormation)
36
+ • Relevant certifications (AWS Solutions Architect, Azure Solutions Architect, etc.)
37
+ • Experience in agile development methodologies
38
+
39
+ Education:
40
+ • Bachelor's degree in Computer Science, Software Engineering, or related field
41
+ • Master's degree preferred
42
+
43
+ We offer competitive compensation, excellent benefits, and the opportunity to work on cutting-edge technologies in a collaborative environment. Join our team and help shape the future of our software platforms!
poor_match_resume.docx ADDED
Binary file (37.8 kB). View file
 
poor_match_resume.docx.txt ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ JENNIFER SMITH
2
+ Frontend Developer
3
+ [email protected] | (555) 234-5678 | linkedin.com/in/jennifersmith
4
+ New York, NY | On-site Only
5
+
6
+ PROFESSIONAL SUMMARY
7
+ Creative Frontend Developer with 3 years of experience crafting engaging user interfaces and responsive web designs. Passionate about creating intuitive user experiences with modern web technologies. Strong attention to detail and commitment to writing clean, maintainable code.
8
+
9
+ SKILLS
10
+ • Frontend: HTML5, CSS3, JavaScript, React, Vue.js
11
+ • Design: Figma, Adobe XD, Photoshop
12
+ • CSS Frameworks: Bootstrap, Tailwind CSS
13
+ • Version Control: Git, GitHub
14
+ • Tools: VS Code, Chrome DevTools
15
+ • Basic Knowledge: Node.js, Express
16
+
17
+ PROFESSIONAL EXPERIENCE
18
+
19
+ FRONTEND DEVELOPER | Creative Web Solutions | 2020 - Present
20
+ • Develop responsive web interfaces using HTML, CSS, and JavaScript
21
+ • Create interactive UI components using React and Vue.js
22
+ • Collaborate with designers to implement pixel-perfect designs
23
+ • Optimize website performance and loading times
24
+ • Ensure cross-browser compatibility and responsive design
25
+ • Implement animations and transitions for enhanced user experience
26
+ • Participate in code reviews and provide feedback to team members
27
+
28
+ JUNIOR WEB DEVELOPER | Digital Design Agency | 2019 - 2020
29
+ • Assisted in the development of client websites using HTML, CSS, and JavaScript
30
+ • Created responsive layouts using Bootstrap
31
+ • Fixed UI bugs and implemented minor features
32
+ • Collaborated with designers to implement visual elements
33
+ • Participated in client meetings and gathered requirements
34
+ • Learned and applied new frontend technologies and frameworks
35
+
36
+ WEB DESIGN INTERN | Tech Startups Inc. | 2018 - 2019
37
+ • Assisted in designing and developing website mockups
38
+ • Created UI elements using Photoshop and Illustrator
39
+ • Implemented basic web pages using HTML and CSS
40
+ • Learned about responsive design principles
41
+ • Participated in team meetings and brainstorming sessions
42
+
43
+ EDUCATION
44
+ • Bachelor of Fine Arts in Graphic Design, New York University, 2018
45
+
46
+ CERTIFICATIONS
47
+ • Meta Frontend Developer Professional Certificate
48
+ • FreeCodeCamp Responsive Web Design Certification
49
+
50
+ PORTFOLIO
51
+ • Personal website: jennifersmith.design
52
+ • GitHub: github.com/jennifersmith
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ openai>=1.0.0
3
+ python-docx>=0.8.11
4
+ docx2txt>=0.8
5
+ PyPDF2>=3.0.0
6
+ plotly>=5.14.0
7
+ python-dotenv>=1.0.0
8
+ requests>=2.28.0
9
+ numpy>=1.24.0
10
+ pandas>=2.0.0
11
+ matplotlib>=3.7.0