Johan713 commited on
Commit
8c9ad61
·
verified ·
1 Parent(s): 677cb76

Update pages/resume_generator.py

Browse files
Files changed (1) hide show
  1. pages/resume_generator.py +454 -453
pages/resume_generator.py CHANGED
@@ -1,454 +1,455 @@
1
- import streamlit as st
2
- import base64
3
- from io import BytesIO
4
- from datetime import datetime
5
- import json
6
- from docx import Document
7
- from docx.shared import Inches, Pt
8
- from docx.enum.text import WD_ALIGN_PARAGRAPH
9
- from reportlab.lib.pagesizes import letter
10
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image
11
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
12
- from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY
13
- from reportlab.lib import colors
14
- from langchain.chat_models import ChatOpenAI
15
- from langchain.schema import HumanMessage
16
- from PIL import Image as PILImage
17
-
18
- AI71_BASE_URL = "https://api.ai71.ai/v1/"
19
- AI71_API_KEY = os.getenv('AI71_API_KEY')
20
-
21
- def get_llm():
22
- return ChatOpenAI(
23
- model="tiiuae/falcon-180B-chat",
24
- api_key=AI71_API_KEY,
25
- base_url=AI71_BASE_URL,
26
- streaming=True,
27
- )
28
-
29
- def generate_resume_content(resume_data):
30
- llm = get_llm()
31
-
32
- prompt = f"""
33
- Generate a highly professional and ATS-optimized resume based on the following information:
34
-
35
- Name: {resume_data['name']}
36
- Email: {resume_data['email']}
37
- Phone: {resume_data['phone']}
38
- Location: {resume_data['location']}
39
-
40
- Work Experience:
41
- {json.dumps(resume_data['work_experience'], indent=2)}
42
-
43
- Education:
44
- {json.dumps(resume_data['education'], indent=2)}
45
-
46
- Skills: {', '.join(resume_data['skills'])}
47
-
48
- Please generate a compelling professional summary and enhance the job descriptions.
49
- Use action verbs, quantify achievements where possible, and highlight key skills.
50
- Ensure the content is tailored for ATS optimization.
51
- The output should be in JSON format with the following structure:
52
- {{
53
- "summary": "Professional summary here",
54
- "work_experience": [
55
- {{
56
- "title": "Job title",
57
- "company": "Company name",
58
- "start_date": "Start date",
59
- "end_date": "End date",
60
- "description": "Enhanced job description with bullet points"
61
- }}
62
- ]
63
- }}
64
- """
65
-
66
- try:
67
- response = llm([HumanMessage(content=prompt)])
68
- enhanced_content = json.loads(response.content)
69
-
70
- resume_data['summary'] = enhanced_content['summary']
71
- resume_data['work_experience'] = enhanced_content['work_experience']
72
-
73
- return resume_data
74
- except Exception as e:
75
- st.error(f"An error occurred while generating AI content: {str(e)}")
76
- return resume_data
77
-
78
- def create_docx(resume_data):
79
- doc = Document()
80
-
81
- # Styles
82
- styles = doc.styles
83
- style = styles.add_style('Name', 1)
84
- style.font.name = 'Calibri'
85
- style.font.size = Pt(24)
86
- style.font.bold = True
87
- style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
88
-
89
- # Add photo if provided
90
- if 'photo' in resume_data and resume_data['photo']:
91
- image_stream = BytesIO(resume_data['photo'])
92
- doc.add_picture(image_stream, width=Inches(2.0))
93
-
94
- # Add name
95
- doc.add_paragraph(resume_data['name'], style='Name')
96
-
97
- # Add contact information
98
- contact_info = doc.add_paragraph()
99
- contact_info.alignment = WD_ALIGN_PARAGRAPH.CENTER
100
- contact_info.add_run(f"{resume_data['email']} | {resume_data['phone']} | {resume_data['location']}")
101
-
102
- # Add summary
103
- doc.add_heading('Professional Summary', level=1)
104
- doc.add_paragraph(resume_data['summary'])
105
-
106
- # Add work experience
107
- doc.add_heading('Work Experience', level=1)
108
- for job in resume_data['work_experience']:
109
- p = doc.add_paragraph(f"{job['title']} at {job['company']}", style='Heading 2')
110
- p.add_run(f"\n{job['start_date']} - {job['end_date']}")
111
- for bullet in job['description'].split('\n'):
112
- if bullet.strip():
113
- doc.add_paragraph(bullet.strip(), style='List Bullet')
114
-
115
- # Add education
116
- doc.add_heading('Education', level=1)
117
- for edu in resume_data['education']:
118
- p = doc.add_paragraph(f"{edu['degree']} in {edu['field']}", style='Heading 2')
119
- p.add_run(f"\n{edu['institution']}, {edu['graduation_date']}")
120
-
121
- # Add skills
122
- doc.add_heading('Skills', level=1)
123
- doc.add_paragraph(', '.join(resume_data['skills']))
124
-
125
- buffer = BytesIO()
126
- doc.save(buffer)
127
- buffer.seek(0)
128
- return buffer
129
-
130
- def create_pdf(resume_data):
131
- buffer = BytesIO()
132
- doc = SimpleDocTemplate(buffer, pagesize=letter, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18)
133
-
134
- styles = getSampleStyleSheet()
135
- styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
136
- styles.add(ParagraphStyle(name='Center', alignment=TA_CENTER))
137
-
138
- story = []
139
-
140
- # Add photo if provided
141
- if 'photo' in resume_data and resume_data['photo']:
142
- image_stream = BytesIO(resume_data['photo'])
143
- img = Image(image_stream, width=100, height=100)
144
- story.append(img)
145
-
146
- # Add name
147
- story.append(Paragraph(resume_data['name'], styles['Title']))
148
-
149
- # Add contact information
150
- story.append(Paragraph(f"{resume_data['email']} | {resume_data['phone']} | {resume_data['location']}", styles['Center']))
151
- story.append(Spacer(1, 12))
152
-
153
- # Add summary
154
- story.append(Paragraph('Professional Summary', styles['Heading1']))
155
- story.append(Paragraph(resume_data['summary'], styles['Justify']))
156
- story.append(Spacer(1, 12))
157
-
158
- # Add work experience
159
- story.append(Paragraph('Work Experience', styles['Heading1']))
160
- for job in resume_data['work_experience']:
161
- story.append(Paragraph(f"{job['title']} at {job['company']}", styles['Heading2']))
162
- story.append(Paragraph(f"{job['start_date']} - {job['end_date']}", styles['Normal']))
163
- for bullet in job['description'].split('\n'):
164
- if bullet.strip():
165
- story.append(Paragraph(f"• {bullet.strip()}", styles['Normal']))
166
- story.append(Spacer(1, 12))
167
-
168
- # Add education
169
- story.append(Paragraph('Education', styles['Heading1']))
170
- for edu in resume_data['education']:
171
- story.append(Paragraph(f"{edu['degree']} in {edu['field']}", styles['Heading2']))
172
- story.append(Paragraph(f"{edu['institution']}, {edu['graduation_date']}", styles['Normal']))
173
- story.append(Spacer(1, 12))
174
-
175
- # Add skills
176
- story.append(Paragraph('Skills', styles['Heading1']))
177
- story.append(Paragraph(', '.join(resume_data['skills']), styles['Normal']))
178
-
179
- doc.build(story)
180
- buffer.seek(0)
181
- return buffer
182
-
183
- def create_txt(resume_data):
184
- txt_content = f"{resume_data['name']}\n"
185
- txt_content += f"{resume_data['email']} | {resume_data['phone']} | {resume_data['location']}\n\n"
186
-
187
- txt_content += "Professional Summary\n"
188
- txt_content += f"{resume_data['summary']}\n\n"
189
-
190
- txt_content += "Work Experience\n"
191
- for job in resume_data['work_experience']:
192
- txt_content += f"{job['title']} at {job['company']}\n"
193
- txt_content += f"{job['start_date']} - {job['end_date']}\n"
194
- for bullet in job['description'].split('\n'):
195
- if bullet.strip():
196
- txt_content += f"• {bullet.strip()}\n"
197
- txt_content += "\n"
198
-
199
- txt_content += "Education\n"
200
- for edu in resume_data['education']:
201
- txt_content += f"{edu['degree']} in {edu['field']}\n"
202
- txt_content += f"{edu['institution']}, {edu['graduation_date']}\n\n"
203
-
204
- txt_content += "Skills\n"
205
- txt_content += ', '.join(resume_data['skills'])
206
-
207
- return txt_content.encode()
208
-
209
- def calculate_ats_score(resume_data):
210
- score = 0
211
- max_score = 100
212
-
213
- # Check for key sections
214
- if resume_data['name']: score += 5
215
- if resume_data['email']: score += 5
216
- if resume_data['phone']: score += 5
217
- if resume_data['location']: score += 5
218
- if resume_data['summary']: score += 10
219
- if resume_data['work_experience']: score += 20
220
- if resume_data['education']: score += 15
221
- if resume_data['skills']: score += 15
222
-
223
- # Check content quality
224
- if len(resume_data['summary'].split()) >= 50: score += 5
225
- if len(resume_data['work_experience']) >= 2: score += 5
226
- if len(resume_data['skills']) >= 5: score += 5
227
-
228
- # Check for keywords (this is a simplified version, in reality, you'd want to check against job-specific keywords)
229
- keywords = ['experience', 'skills', 'project', 'team', 'leadership', 'communication', 'achieved', 'improved', 'managed', 'developed']
230
- resume_text = ' '.join([str(value) for value in resume_data.values() if isinstance(value, str)])
231
- for keyword in keywords:
232
- if keyword in resume_text.lower():
233
- score += 1
234
-
235
- return min(score, max_score)
236
-
237
- def main():
238
- st.set_page_config(page_title="AI-Enhanced Resume Builder", page_icon="📄", layout="wide")
239
-
240
- st.markdown("""
241
- <style>
242
- .big-font {
243
- font-size:30px !important;
244
- font-weight: bold;
245
- }
246
- .stButton>button {
247
- width: 100%;
248
- }
249
- </style>
250
- """, unsafe_allow_html=True)
251
-
252
- # Add sidebar
253
- st.sidebar.title("About This Project")
254
- st.sidebar.write("""
255
- Welcome to the AI-Enhanced Resume Builder!
256
-
257
- This project helps you create a professional, ATS-optimized resume with the power of AI. Here's what you can do:
258
-
259
- 1. Input your personal information
260
- 2. Add your work experience
261
- 3. Include your education details
262
- 4. List your skills
263
- 5. Optionally upload a photo
264
- 6. Generate AI-enhanced content
265
- 7. Review and download your resume
266
-
267
- The AI will help improve your resume content and provide an ATS compatibility score.
268
-
269
- Get started by filling out the form and clicking 'Next' at each step!
270
- """)
271
-
272
- st.markdown('<p class="big-font">AI-Enhanced Resume Builder</p>', unsafe_allow_html=True)
273
- st.write("Create a professional, ATS-optimized resume with AI-powered content enhancement")
274
-
275
- # Initialize session state
276
- if 'step' not in st.session_state:
277
- st.session_state.step = 1
278
-
279
- if 'resume_data' not in st.session_state:
280
- st.session_state.resume_data = {
281
- 'name': '', 'email': '', 'phone': '', 'location': '',
282
- 'summary': '', 'work_experience': [], 'education': [], 'skills': [], 'photo': None
283
- }
284
-
285
- # Step 1: Personal Information
286
- if st.session_state.step == 1:
287
- st.subheader("Step 1: Personal Information")
288
- name = st.text_input("Full Name", st.session_state.resume_data['name'])
289
- email = st.text_input("Email", st.session_state.resume_data['email'])
290
- phone = st.text_input("Phone", st.session_state.resume_data['phone'])
291
- location = st.text_input("Location", st.session_state.resume_data['location'])
292
-
293
- photo_upload = st.file_uploader("Upload a photo (optional)", type=['jpg', 'jpeg', 'png'])
294
- if photo_upload:
295
- image = PILImage.open(photo_upload)
296
- st.image(image, caption='Uploaded Image', use_column_width=True)
297
- buffered = BytesIO()
298
- image.save(buffered, format="PNG")
299
- st.session_state.resume_data['photo'] = buffered.getvalue()
300
-
301
- if st.button("Next"):
302
- if name and email and phone and location:
303
- st.session_state.resume_data.update({
304
- 'name': name,
305
- 'email': email,
306
- 'phone': phone,
307
- 'location': location
308
- })
309
- st.session_state.step = 2
310
- else:
311
- st.error("Please fill in all required fields before proceeding.")
312
-
313
- # Step 2: Work Experience
314
- elif st.session_state.step == 2:
315
- st.subheader("Step 2: Work Experience")
316
- num_jobs = st.number_input("Number of jobs to add", min_value=1, max_value=10, value=len(st.session_state.resume_data['work_experience']) or 1)
317
-
318
- work_experience = []
319
- for i in range(num_jobs):
320
- st.write(f"Job {i+1}")
321
- job = {}
322
- job['title'] = st.text_input(f"Job Title {i+1}", st.session_state.resume_data['work_experience'][i]['title'] if i < len(st.session_state.resume_data['work_experience']) else '')
323
- job['company'] = st.text_input(f"Company {i+1}", st.session_state.resume_data['work_experience'][i]['company'] if i < len(st.session_state.resume_data['work_experience']) else '')
324
- job['start_date'] = st.date_input(f"Start Date {i+1}", value=datetime.strptime(st.session_state.resume_data['work_experience'][i]['start_date'] if i < len(st.session_state.resume_data['work_experience']) else '2020-01-01', '%Y-%m-%d')).strftime('%Y-%m-%d')
325
- job['end_date'] = st.date_input(f"End Date {i+1}", value=datetime.strptime(st.session_state.resume_data['work_experience'][i]['end_date'] if i < len(st.session_state.resume_data['work_experience']) else '2023-01-01', '%Y-%m-%d')).strftime('%Y-%m-%d')
326
- job['description'] = st.text_area(f"Job Description {i+1}", st.session_state.resume_data['work_experience'][i]['description'] if i < len(st.session_state.resume_data['work_experience']) else '', height=100)
327
- work_experience.append(job)
328
-
329
- col1, col2 = st.columns(2)
330
- if col1.button("Previous"):
331
- st.session_state.step = 1
332
- if col2.button("Next"):
333
- if all(job['title'] and job['company'] and job['description'] for job in work_experience):
334
- st.session_state.resume_data['work_experience'] = work_experience
335
- st.session_state.step = 3
336
- else:
337
- st.error("Please fill in all required fields for each job before proceeding.")
338
-
339
- # Step 3: Education
340
- elif st.session_state.step == 3:
341
- st.subheader("Step 3: Education")
342
- num_edu = st.number_input("Number of education entries", min_value=1, max_value=5, value=len(st.session_state.resume_data['education']) or 1)
343
-
344
- education = []
345
- for i in range(num_edu):
346
- st.write(f"Education {i+1}")
347
- edu = {}
348
- edu['degree'] = st.text_input(f"Degree {i+1}", st.session_state.resume_data['education'][i]['degree'] if i < len(st.session_state.resume_data['education']) else '')
349
- edu['field'] = st.text_input(f"Field of Study {i+1}", st.session_state.resume_data['education'][i]['field'] if i < len(st.session_state.resume_data['education']) else '')
350
- edu['institution'] = st.text_input(f"Institution {i+1}", st.session_state.resume_data['education'][i]['institution'] if i < len(st.session_state.resume_data['education']) else '')
351
- edu['graduation_date'] = st.date_input(f"Graduation Date {i+1}", value=datetime.strptime(st.session_state.resume_data['education'][i]['graduation_date'] if i < len(st.session_state.resume_data['education']) else '2023-01-01', '%Y-%m-%d')).strftime('%Y-%m-%d')
352
- education.append(edu)
353
-
354
- col1, col2 = st.columns(2)
355
- if col1.button("Previous"):
356
- st.session_state.step = 2
357
- if col2.button("Next"):
358
- if all(edu['degree'] and edu['field'] and edu['institution'] for edu in education):
359
- st.session_state.resume_data['education'] = education
360
- st.session_state.step = 4
361
- else:
362
- st.error("Please fill in all required fields for each education entry before proceeding.")
363
-
364
- # Step 4: Skills and Generation
365
- elif st.session_state.step == 4:
366
- st.subheader("Step 4: Skills and Resume Generation")
367
- skills_input = st.text_input("Skills (comma-separated)", ', '.join(st.session_state.resume_data['skills']))
368
-
369
- if st.button("Generate Resume"):
370
- if skills_input.strip():
371
- st.session_state.resume_data['skills'] = [skill.strip() for skill in skills_input.split(',') if skill.strip()]
372
- with st.spinner("Generating AI-enhanced resume content..."):
373
- st.session_state.resume_data = generate_resume_content(st.session_state.resume_data)
374
- st.session_state.step = 5
375
- st.experimental_rerun()
376
- else:
377
- st.error("Please enter at least one skill before generating the resume.")
378
-
379
- # Step 5: Review and Download
380
- elif st.session_state.step == 5:
381
- st.subheader("Generated Resume")
382
-
383
- # Display resume content for review
384
- st.write("### Personal Information")
385
- st.write(f"**Name:** {st.session_state.resume_data['name']}")
386
- st.write(f"**Email:** {st.session_state.resume_data['email']}")
387
- st.write(f"**Phone:** {st.session_state.resume_data['phone']}")
388
- st.write(f"**Location:** {st.session_state.resume_data['location']}")
389
-
390
- if st.session_state.resume_data['photo']:
391
- st.image(st.session_state.resume_data['photo'], caption='Your Photo', width=200)
392
-
393
- st.write("### Professional Summary")
394
- st.write(st.session_state.resume_data['summary'])
395
-
396
- st.write("### Work Experience")
397
- for job in st.session_state.resume_data['work_experience']:
398
- st.write(f"**{job['title']} at {job['company']}**")
399
- st.write(f"{job['start_date']} - {job['end_date']}")
400
- st.write(job['description'])
401
-
402
- st.write("### Education")
403
- for edu in st.session_state.resume_data['education']:
404
- st.write(f"**{edu['degree']} in {edu['field']}**")
405
- st.write(f"{edu['institution']}, {edu['graduation_date']}")
406
-
407
- st.write("### Skills")
408
- st.write(', '.join(st.session_state.resume_data['skills']))
409
-
410
- # Calculate and display ATS score
411
- ats_score = calculate_ats_score(st.session_state.resume_data)
412
- st.write(f"### ATS Compatibility Score: {ats_score}%")
413
-
414
- # Download options
415
- st.write("### Download Options")
416
- col1, col2, col3 = st.columns(3)
417
-
418
- docx_buffer = create_docx(st.session_state.resume_data)
419
- col1.download_button(
420
- label="Download as DOCX",
421
- data=docx_buffer,
422
- file_name="resume.docx",
423
- mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
424
- )
425
-
426
- pdf_buffer = create_pdf(st.session_state.resume_data)
427
- col2.download_button(
428
- label="Download as PDF",
429
- data=pdf_buffer,
430
- file_name="resume.pdf",
431
- mime="application/pdf"
432
- )
433
-
434
- txt_content = create_txt(st.session_state.resume_data)
435
- col3.download_button(
436
- label="Download as TXT",
437
- data=txt_content,
438
- file_name="resume.txt",
439
- mime="text/plain"
440
- )
441
-
442
- if st.button("Edit Resume"):
443
- st.session_state.step = 1
444
-
445
- if st.button("Start Over"):
446
- st.session_state.step = 1
447
- st.session_state.resume_data = {
448
- 'name': '', 'email': '', 'phone': '', 'location': '',
449
- 'summary': '', 'work_experience': [], 'education': [], 'skills': [], 'photo': None
450
- }
451
- st.experimental_rerun()
452
-
453
- if __name__ == "__main__":
 
454
  main()
 
1
+ import streamlit as st
2
+ import base64
3
+ import os
4
+ from io import BytesIO
5
+ from datetime import datetime
6
+ import json
7
+ from docx import Document
8
+ from docx.shared import Inches, Pt
9
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
10
+ from reportlab.lib.pagesizes import letter
11
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image
12
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
13
+ from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY
14
+ from reportlab.lib import colors
15
+ from langchain.chat_models import ChatOpenAI
16
+ from langchain.schema import HumanMessage
17
+ from PIL import Image as PILImage
18
+
19
+ AI71_BASE_URL = "https://api.ai71.ai/v1/"
20
+ AI71_API_KEY = os.getenv('AI71_API_KEY')
21
+
22
+ def get_llm():
23
+ return ChatOpenAI(
24
+ model="tiiuae/falcon-180B-chat",
25
+ api_key=AI71_API_KEY,
26
+ base_url=AI71_BASE_URL,
27
+ streaming=True,
28
+ )
29
+
30
+ def generate_resume_content(resume_data):
31
+ llm = get_llm()
32
+
33
+ prompt = f"""
34
+ Generate a highly professional and ATS-optimized resume based on the following information:
35
+
36
+ Name: {resume_data['name']}
37
+ Email: {resume_data['email']}
38
+ Phone: {resume_data['phone']}
39
+ Location: {resume_data['location']}
40
+
41
+ Work Experience:
42
+ {json.dumps(resume_data['work_experience'], indent=2)}
43
+
44
+ Education:
45
+ {json.dumps(resume_data['education'], indent=2)}
46
+
47
+ Skills: {', '.join(resume_data['skills'])}
48
+
49
+ Please generate a compelling professional summary and enhance the job descriptions.
50
+ Use action verbs, quantify achievements where possible, and highlight key skills.
51
+ Ensure the content is tailored for ATS optimization.
52
+ The output should be in JSON format with the following structure:
53
+ {{
54
+ "summary": "Professional summary here",
55
+ "work_experience": [
56
+ {{
57
+ "title": "Job title",
58
+ "company": "Company name",
59
+ "start_date": "Start date",
60
+ "end_date": "End date",
61
+ "description": "Enhanced job description with bullet points"
62
+ }}
63
+ ]
64
+ }}
65
+ """
66
+
67
+ try:
68
+ response = llm([HumanMessage(content=prompt)])
69
+ enhanced_content = json.loads(response.content)
70
+
71
+ resume_data['summary'] = enhanced_content['summary']
72
+ resume_data['work_experience'] = enhanced_content['work_experience']
73
+
74
+ return resume_data
75
+ except Exception as e:
76
+ st.error(f"An error occurred while generating AI content: {str(e)}")
77
+ return resume_data
78
+
79
+ def create_docx(resume_data):
80
+ doc = Document()
81
+
82
+ # Styles
83
+ styles = doc.styles
84
+ style = styles.add_style('Name', 1)
85
+ style.font.name = 'Calibri'
86
+ style.font.size = Pt(24)
87
+ style.font.bold = True
88
+ style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
89
+
90
+ # Add photo if provided
91
+ if 'photo' in resume_data and resume_data['photo']:
92
+ image_stream = BytesIO(resume_data['photo'])
93
+ doc.add_picture(image_stream, width=Inches(2.0))
94
+
95
+ # Add name
96
+ doc.add_paragraph(resume_data['name'], style='Name')
97
+
98
+ # Add contact information
99
+ contact_info = doc.add_paragraph()
100
+ contact_info.alignment = WD_ALIGN_PARAGRAPH.CENTER
101
+ contact_info.add_run(f"{resume_data['email']} | {resume_data['phone']} | {resume_data['location']}")
102
+
103
+ # Add summary
104
+ doc.add_heading('Professional Summary', level=1)
105
+ doc.add_paragraph(resume_data['summary'])
106
+
107
+ # Add work experience
108
+ doc.add_heading('Work Experience', level=1)
109
+ for job in resume_data['work_experience']:
110
+ p = doc.add_paragraph(f"{job['title']} at {job['company']}", style='Heading 2')
111
+ p.add_run(f"\n{job['start_date']} - {job['end_date']}")
112
+ for bullet in job['description'].split('\n'):
113
+ if bullet.strip():
114
+ doc.add_paragraph(bullet.strip(), style='List Bullet')
115
+
116
+ # Add education
117
+ doc.add_heading('Education', level=1)
118
+ for edu in resume_data['education']:
119
+ p = doc.add_paragraph(f"{edu['degree']} in {edu['field']}", style='Heading 2')
120
+ p.add_run(f"\n{edu['institution']}, {edu['graduation_date']}")
121
+
122
+ # Add skills
123
+ doc.add_heading('Skills', level=1)
124
+ doc.add_paragraph(', '.join(resume_data['skills']))
125
+
126
+ buffer = BytesIO()
127
+ doc.save(buffer)
128
+ buffer.seek(0)
129
+ return buffer
130
+
131
+ def create_pdf(resume_data):
132
+ buffer = BytesIO()
133
+ doc = SimpleDocTemplate(buffer, pagesize=letter, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18)
134
+
135
+ styles = getSampleStyleSheet()
136
+ styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
137
+ styles.add(ParagraphStyle(name='Center', alignment=TA_CENTER))
138
+
139
+ story = []
140
+
141
+ # Add photo if provided
142
+ if 'photo' in resume_data and resume_data['photo']:
143
+ image_stream = BytesIO(resume_data['photo'])
144
+ img = Image(image_stream, width=100, height=100)
145
+ story.append(img)
146
+
147
+ # Add name
148
+ story.append(Paragraph(resume_data['name'], styles['Title']))
149
+
150
+ # Add contact information
151
+ story.append(Paragraph(f"{resume_data['email']} | {resume_data['phone']} | {resume_data['location']}", styles['Center']))
152
+ story.append(Spacer(1, 12))
153
+
154
+ # Add summary
155
+ story.append(Paragraph('Professional Summary', styles['Heading1']))
156
+ story.append(Paragraph(resume_data['summary'], styles['Justify']))
157
+ story.append(Spacer(1, 12))
158
+
159
+ # Add work experience
160
+ story.append(Paragraph('Work Experience', styles['Heading1']))
161
+ for job in resume_data['work_experience']:
162
+ story.append(Paragraph(f"{job['title']} at {job['company']}", styles['Heading2']))
163
+ story.append(Paragraph(f"{job['start_date']} - {job['end_date']}", styles['Normal']))
164
+ for bullet in job['description'].split('\n'):
165
+ if bullet.strip():
166
+ story.append(Paragraph(f"• {bullet.strip()}", styles['Normal']))
167
+ story.append(Spacer(1, 12))
168
+
169
+ # Add education
170
+ story.append(Paragraph('Education', styles['Heading1']))
171
+ for edu in resume_data['education']:
172
+ story.append(Paragraph(f"{edu['degree']} in {edu['field']}", styles['Heading2']))
173
+ story.append(Paragraph(f"{edu['institution']}, {edu['graduation_date']}", styles['Normal']))
174
+ story.append(Spacer(1, 12))
175
+
176
+ # Add skills
177
+ story.append(Paragraph('Skills', styles['Heading1']))
178
+ story.append(Paragraph(', '.join(resume_data['skills']), styles['Normal']))
179
+
180
+ doc.build(story)
181
+ buffer.seek(0)
182
+ return buffer
183
+
184
+ def create_txt(resume_data):
185
+ txt_content = f"{resume_data['name']}\n"
186
+ txt_content += f"{resume_data['email']} | {resume_data['phone']} | {resume_data['location']}\n\n"
187
+
188
+ txt_content += "Professional Summary\n"
189
+ txt_content += f"{resume_data['summary']}\n\n"
190
+
191
+ txt_content += "Work Experience\n"
192
+ for job in resume_data['work_experience']:
193
+ txt_content += f"{job['title']} at {job['company']}\n"
194
+ txt_content += f"{job['start_date']} - {job['end_date']}\n"
195
+ for bullet in job['description'].split('\n'):
196
+ if bullet.strip():
197
+ txt_content += f"• {bullet.strip()}\n"
198
+ txt_content += "\n"
199
+
200
+ txt_content += "Education\n"
201
+ for edu in resume_data['education']:
202
+ txt_content += f"{edu['degree']} in {edu['field']}\n"
203
+ txt_content += f"{edu['institution']}, {edu['graduation_date']}\n\n"
204
+
205
+ txt_content += "Skills\n"
206
+ txt_content += ', '.join(resume_data['skills'])
207
+
208
+ return txt_content.encode()
209
+
210
+ def calculate_ats_score(resume_data):
211
+ score = 0
212
+ max_score = 100
213
+
214
+ # Check for key sections
215
+ if resume_data['name']: score += 5
216
+ if resume_data['email']: score += 5
217
+ if resume_data['phone']: score += 5
218
+ if resume_data['location']: score += 5
219
+ if resume_data['summary']: score += 10
220
+ if resume_data['work_experience']: score += 20
221
+ if resume_data['education']: score += 15
222
+ if resume_data['skills']: score += 15
223
+
224
+ # Check content quality
225
+ if len(resume_data['summary'].split()) >= 50: score += 5
226
+ if len(resume_data['work_experience']) >= 2: score += 5
227
+ if len(resume_data['skills']) >= 5: score += 5
228
+
229
+ # Check for keywords (this is a simplified version, in reality, you'd want to check against job-specific keywords)
230
+ keywords = ['experience', 'skills', 'project', 'team', 'leadership', 'communication', 'achieved', 'improved', 'managed', 'developed']
231
+ resume_text = ' '.join([str(value) for value in resume_data.values() if isinstance(value, str)])
232
+ for keyword in keywords:
233
+ if keyword in resume_text.lower():
234
+ score += 1
235
+
236
+ return min(score, max_score)
237
+
238
+ def main():
239
+ st.set_page_config(page_title="AI-Enhanced Resume Builder", page_icon="📄", layout="wide")
240
+
241
+ st.markdown("""
242
+ <style>
243
+ .big-font {
244
+ font-size:30px !important;
245
+ font-weight: bold;
246
+ }
247
+ .stButton>button {
248
+ width: 100%;
249
+ }
250
+ </style>
251
+ """, unsafe_allow_html=True)
252
+
253
+ # Add sidebar
254
+ st.sidebar.title("About This Project")
255
+ st.sidebar.write("""
256
+ Welcome to the AI-Enhanced Resume Builder!
257
+
258
+ This project helps you create a professional, ATS-optimized resume with the power of AI. Here's what you can do:
259
+
260
+ 1. Input your personal information
261
+ 2. Add your work experience
262
+ 3. Include your education details
263
+ 4. List your skills
264
+ 5. Optionally upload a photo
265
+ 6. Generate AI-enhanced content
266
+ 7. Review and download your resume
267
+
268
+ The AI will help improve your resume content and provide an ATS compatibility score.
269
+
270
+ Get started by filling out the form and clicking 'Next' at each step!
271
+ """)
272
+
273
+ st.markdown('<p class="big-font">AI-Enhanced Resume Builder</p>', unsafe_allow_html=True)
274
+ st.write("Create a professional, ATS-optimized resume with AI-powered content enhancement")
275
+
276
+ # Initialize session state
277
+ if 'step' not in st.session_state:
278
+ st.session_state.step = 1
279
+
280
+ if 'resume_data' not in st.session_state:
281
+ st.session_state.resume_data = {
282
+ 'name': '', 'email': '', 'phone': '', 'location': '',
283
+ 'summary': '', 'work_experience': [], 'education': [], 'skills': [], 'photo': None
284
+ }
285
+
286
+ # Step 1: Personal Information
287
+ if st.session_state.step == 1:
288
+ st.subheader("Step 1: Personal Information")
289
+ name = st.text_input("Full Name", st.session_state.resume_data['name'])
290
+ email = st.text_input("Email", st.session_state.resume_data['email'])
291
+ phone = st.text_input("Phone", st.session_state.resume_data['phone'])
292
+ location = st.text_input("Location", st.session_state.resume_data['location'])
293
+
294
+ photo_upload = st.file_uploader("Upload a photo (optional)", type=['jpg', 'jpeg', 'png'])
295
+ if photo_upload:
296
+ image = PILImage.open(photo_upload)
297
+ st.image(image, caption='Uploaded Image', use_column_width=True)
298
+ buffered = BytesIO()
299
+ image.save(buffered, format="PNG")
300
+ st.session_state.resume_data['photo'] = buffered.getvalue()
301
+
302
+ if st.button("Next"):
303
+ if name and email and phone and location:
304
+ st.session_state.resume_data.update({
305
+ 'name': name,
306
+ 'email': email,
307
+ 'phone': phone,
308
+ 'location': location
309
+ })
310
+ st.session_state.step = 2
311
+ else:
312
+ st.error("Please fill in all required fields before proceeding.")
313
+
314
+ # Step 2: Work Experience
315
+ elif st.session_state.step == 2:
316
+ st.subheader("Step 2: Work Experience")
317
+ num_jobs = st.number_input("Number of jobs to add", min_value=1, max_value=10, value=len(st.session_state.resume_data['work_experience']) or 1)
318
+
319
+ work_experience = []
320
+ for i in range(num_jobs):
321
+ st.write(f"Job {i+1}")
322
+ job = {}
323
+ job['title'] = st.text_input(f"Job Title {i+1}", st.session_state.resume_data['work_experience'][i]['title'] if i < len(st.session_state.resume_data['work_experience']) else '')
324
+ job['company'] = st.text_input(f"Company {i+1}", st.session_state.resume_data['work_experience'][i]['company'] if i < len(st.session_state.resume_data['work_experience']) else '')
325
+ job['start_date'] = st.date_input(f"Start Date {i+1}", value=datetime.strptime(st.session_state.resume_data['work_experience'][i]['start_date'] if i < len(st.session_state.resume_data['work_experience']) else '2020-01-01', '%Y-%m-%d')).strftime('%Y-%m-%d')
326
+ job['end_date'] = st.date_input(f"End Date {i+1}", value=datetime.strptime(st.session_state.resume_data['work_experience'][i]['end_date'] if i < len(st.session_state.resume_data['work_experience']) else '2023-01-01', '%Y-%m-%d')).strftime('%Y-%m-%d')
327
+ job['description'] = st.text_area(f"Job Description {i+1}", st.session_state.resume_data['work_experience'][i]['description'] if i < len(st.session_state.resume_data['work_experience']) else '', height=100)
328
+ work_experience.append(job)
329
+
330
+ col1, col2 = st.columns(2)
331
+ if col1.button("Previous"):
332
+ st.session_state.step = 1
333
+ if col2.button("Next"):
334
+ if all(job['title'] and job['company'] and job['description'] for job in work_experience):
335
+ st.session_state.resume_data['work_experience'] = work_experience
336
+ st.session_state.step = 3
337
+ else:
338
+ st.error("Please fill in all required fields for each job before proceeding.")
339
+
340
+ # Step 3: Education
341
+ elif st.session_state.step == 3:
342
+ st.subheader("Step 3: Education")
343
+ num_edu = st.number_input("Number of education entries", min_value=1, max_value=5, value=len(st.session_state.resume_data['education']) or 1)
344
+
345
+ education = []
346
+ for i in range(num_edu):
347
+ st.write(f"Education {i+1}")
348
+ edu = {}
349
+ edu['degree'] = st.text_input(f"Degree {i+1}", st.session_state.resume_data['education'][i]['degree'] if i < len(st.session_state.resume_data['education']) else '')
350
+ edu['field'] = st.text_input(f"Field of Study {i+1}", st.session_state.resume_data['education'][i]['field'] if i < len(st.session_state.resume_data['education']) else '')
351
+ edu['institution'] = st.text_input(f"Institution {i+1}", st.session_state.resume_data['education'][i]['institution'] if i < len(st.session_state.resume_data['education']) else '')
352
+ edu['graduation_date'] = st.date_input(f"Graduation Date {i+1}", value=datetime.strptime(st.session_state.resume_data['education'][i]['graduation_date'] if i < len(st.session_state.resume_data['education']) else '2023-01-01', '%Y-%m-%d')).strftime('%Y-%m-%d')
353
+ education.append(edu)
354
+
355
+ col1, col2 = st.columns(2)
356
+ if col1.button("Previous"):
357
+ st.session_state.step = 2
358
+ if col2.button("Next"):
359
+ if all(edu['degree'] and edu['field'] and edu['institution'] for edu in education):
360
+ st.session_state.resume_data['education'] = education
361
+ st.session_state.step = 4
362
+ else:
363
+ st.error("Please fill in all required fields for each education entry before proceeding.")
364
+
365
+ # Step 4: Skills and Generation
366
+ elif st.session_state.step == 4:
367
+ st.subheader("Step 4: Skills and Resume Generation")
368
+ skills_input = st.text_input("Skills (comma-separated)", ', '.join(st.session_state.resume_data['skills']))
369
+
370
+ if st.button("Generate Resume"):
371
+ if skills_input.strip():
372
+ st.session_state.resume_data['skills'] = [skill.strip() for skill in skills_input.split(',') if skill.strip()]
373
+ with st.spinner("Generating AI-enhanced resume content..."):
374
+ st.session_state.resume_data = generate_resume_content(st.session_state.resume_data)
375
+ st.session_state.step = 5
376
+ st.experimental_rerun()
377
+ else:
378
+ st.error("Please enter at least one skill before generating the resume.")
379
+
380
+ # Step 5: Review and Download
381
+ elif st.session_state.step == 5:
382
+ st.subheader("Generated Resume")
383
+
384
+ # Display resume content for review
385
+ st.write("### Personal Information")
386
+ st.write(f"**Name:** {st.session_state.resume_data['name']}")
387
+ st.write(f"**Email:** {st.session_state.resume_data['email']}")
388
+ st.write(f"**Phone:** {st.session_state.resume_data['phone']}")
389
+ st.write(f"**Location:** {st.session_state.resume_data['location']}")
390
+
391
+ if st.session_state.resume_data['photo']:
392
+ st.image(st.session_state.resume_data['photo'], caption='Your Photo', width=200)
393
+
394
+ st.write("### Professional Summary")
395
+ st.write(st.session_state.resume_data['summary'])
396
+
397
+ st.write("### Work Experience")
398
+ for job in st.session_state.resume_data['work_experience']:
399
+ st.write(f"**{job['title']} at {job['company']}**")
400
+ st.write(f"{job['start_date']} - {job['end_date']}")
401
+ st.write(job['description'])
402
+
403
+ st.write("### Education")
404
+ for edu in st.session_state.resume_data['education']:
405
+ st.write(f"**{edu['degree']} in {edu['field']}**")
406
+ st.write(f"{edu['institution']}, {edu['graduation_date']}")
407
+
408
+ st.write("### Skills")
409
+ st.write(', '.join(st.session_state.resume_data['skills']))
410
+
411
+ # Calculate and display ATS score
412
+ ats_score = calculate_ats_score(st.session_state.resume_data)
413
+ st.write(f"### ATS Compatibility Score: {ats_score}%")
414
+
415
+ # Download options
416
+ st.write("### Download Options")
417
+ col1, col2, col3 = st.columns(3)
418
+
419
+ docx_buffer = create_docx(st.session_state.resume_data)
420
+ col1.download_button(
421
+ label="Download as DOCX",
422
+ data=docx_buffer,
423
+ file_name="resume.docx",
424
+ mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
425
+ )
426
+
427
+ pdf_buffer = create_pdf(st.session_state.resume_data)
428
+ col2.download_button(
429
+ label="Download as PDF",
430
+ data=pdf_buffer,
431
+ file_name="resume.pdf",
432
+ mime="application/pdf"
433
+ )
434
+
435
+ txt_content = create_txt(st.session_state.resume_data)
436
+ col3.download_button(
437
+ label="Download as TXT",
438
+ data=txt_content,
439
+ file_name="resume.txt",
440
+ mime="text/plain"
441
+ )
442
+
443
+ if st.button("Edit Resume"):
444
+ st.session_state.step = 1
445
+
446
+ if st.button("Start Over"):
447
+ st.session_state.step = 1
448
+ st.session_state.resume_data = {
449
+ 'name': '', 'email': '', 'phone': '', 'location': '',
450
+ 'summary': '', 'work_experience': [], 'education': [], 'skills': [], 'photo': None
451
+ }
452
+ st.experimental_rerun()
453
+
454
+ if __name__ == "__main__":
455
  main()