AreejMehboob commited on
Commit
c43747d
·
verified ·
1 Parent(s): aea02ba

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +438 -0
src/streamlit_app.py CHANGED
@@ -1,3 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os, types, streamlit as st
2
  import os
3
  os.environ['STREAMLIT_CONFIG_DIR'] = '/tmp/.streamlit'
@@ -19,3 +41,419 @@ if app_code:
19
  else:
20
  st.error("APP_CODE is empty. Did you set it?")
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import os, types, streamlit as st
2
+ # import os
3
+ # os.environ['STREAMLIT_CONFIG_DIR'] = '/tmp/.streamlit'
4
+ # # Fetch the hidden code from env var
5
+ # app_code = os.environ.get("APP_CODE", "")
6
+
7
+ # def execute_code(code_str):
8
+ # module = types.ModuleType("dynamic_app")
9
+ # try:
10
+ # exec(code_str, module.__dict__)
11
+ # if hasattr(module, "main"):
12
+ # module.main()
13
+ # except Exception as e:
14
+ # st.error(f"Error in hidden code: {e}")
15
+
16
+ # if app_code:
17
+ # execute_code(app_code)
18
+
19
+ # else:
20
+ # st.error("APP_CODE is empty. Did you set it?")
21
+
22
+
23
  import os, types, streamlit as st
24
  import os
25
  os.environ['STREAMLIT_CONFIG_DIR'] = '/tmp/.streamlit'
 
41
  else:
42
  st.error("APP_CODE is empty. Did you set it?")
43
 
44
+ def main():
45
+ st.set_page_config(layout="wide")
46
+
47
+ # Sidebar for navigation
48
+ with st.sidebar:
49
+ # About section with blue background
50
+ st.markdown("""
51
+ <div style="background-color:#3299a8; padding:10px; border-radius:5px;">
52
+ <h3 style="color:white;">About</h3>
53
+ <p style="color:white;">
54
+ This Resume Matching System helps you find the best candidates for your job openings.
55
+ Simply input your job description and requirements, and our AI-powered system will
56
+ analyze and rank resumes based on skill match, experience, and overall fit.
57
+ </p>
58
+ </div>
59
+ """, unsafe_allow_html=True)
60
+
61
+ # How it Works section with light blue background
62
+ st.markdown("""
63
+ <div style="background-color:#e6f3f5; padding:10px; border-radius:5px; margin-top:10px;">
64
+ <h3 style="color:#3299a8;">How it Works</h3>
65
+ <ul style="margin-left: 15px; padding-left: 10px;">
66
+ <li>Enter your job description or select a sample</li>
67
+ <li>Our AI-agent analyzes key skills and requirements</li>
68
+ <li>View ranked candidates with match percentages</li>
69
+ <li>Examine detailed skill comparisons for each resume</li>
70
+ </ul>
71
+ </div>
72
+ """, unsafe_allow_html=True)
73
+
74
+ # Sample Job Descriptions section
75
+ st.markdown("<h3 style='font-size:1.2em;'>Sample Job Descriptions</h3>", unsafe_allow_html=True)
76
+
77
+ # Display sample job descriptions in sidebar with smaller font
78
+ st.write("Click any job to prefill the form:")
79
+
80
+ # Create a container with custom CSS for smaller buttons
81
+ st.markdown("""
82
+ <style>
83
+ div.stButton > button {
84
+ font-size: 0.8em;
85
+ padding: 2px 8px;
86
+ margin: 3px 0px;
87
+ width: 100%;
88
+ text-align: left;
89
+ }
90
+ .resume-card {
91
+ background-color: #f8f9fa;
92
+ border-radius: 10px;
93
+ padding: 15px;
94
+ margin-bottom: 20px;
95
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
96
+ }
97
+ .card-header {
98
+ border-bottom: 1px solid #e0e0e0;
99
+ padding-bottom: 10px;
100
+ margin-bottom: 15px;
101
+ }
102
+ .contact-section {
103
+ background-color: #e6f3f5;
104
+ padding: 10px;
105
+ border-radius: 5px;
106
+ margin-top: 10px;
107
+ }
108
+ .skill-badge {
109
+ background-color: #3299a8;
110
+ color: white;
111
+ padding: 4px 8px;
112
+ border-radius: 15px;
113
+ margin: 2px;
114
+ display: inline-block;
115
+ font-size: 0.85em;
116
+ }
117
+ .missing-skill-badge {
118
+ background-color: #6c757d;
119
+ color: white;
120
+ padding: 4px 8px;
121
+ border-radius: 15px;
122
+ margin: 2px;
123
+ display: inline-block;
124
+ font-size: 0.85em;
125
+ }
126
+ .experience-card {
127
+ background-color: #f8f9fa;
128
+ border-left: 3px solid #3299a8;
129
+ padding: 10px;
130
+ margin-bottom: 10px;
131
+ border-radius: 0 5px 5px 0;
132
+ }
133
+ </style>
134
+ """, unsafe_allow_html=True)
135
+
136
+ # Add horizontal line before job listings
137
+ st.markdown("<hr>", unsafe_allow_html=True)
138
+
139
+ # Job buttons
140
+ for i, job in enumerate(job_posts):
141
+ if st.button(job["title"], key=f"job_{i}"):
142
+ # This will be used to set the job description text area
143
+ st.session_state.job_description = job["jd"]
144
+
145
+ # Main content area
146
+ st.title("Resume Matching System")
147
+
148
+ # Initialize session state for job description if it doesn't exist
149
+ if 'job_description' not in st.session_state:
150
+ st.session_state.job_description = "Enter job description here..."
151
+
152
+ # Input fields - using session state for job description
153
+ job_description = st.text_area("Job Description", value=st.session_state.job_description, height=250)
154
+ additional_requirements = st.text_area("Additional Requirements", "", height=100)
155
+
156
+ # Create two columns for the numeric inputs
157
+ limit = st.slider("Number of Results", min_value=1, max_value=10, value=5, step=1)
158
+
159
+ # Search button and animation container
160
+ search_button = st.button("Search Resumes", type="primary")
161
+
162
+ # Create a container for the code animation
163
+ code_container = st.container()
164
+
165
+ # Add custom CSS for the code animation box
166
+ st.markdown("""
167
+ <style>
168
+ .highlight {
169
+ background-color: black !important;
170
+ }
171
+ .highlight pre {
172
+ color: yellow !important;
173
+ }
174
+ .highlight .gp, .highlight .gu, .highlight .gt {
175
+ color: yellow !important;
176
+ }
177
+ pre code {
178
+ white-space: pre-wrap !important;
179
+ }
180
+ </style>
181
+ """, unsafe_allow_html=True)
182
+ st.markdown("""
183
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
184
+ """, unsafe_allow_html=True)
185
+ if search_button:
186
+
187
+ # Prepare the API request
188
+
189
+ url = f"{api_endpoint}/resumes/search"
190
+ headers = {
191
+ "accept": "application/json",
192
+ "Content-Type": "application/json",
193
+ "Authorization": hf_token
194
+ }
195
+ payload = {
196
+ "job_description": job_description,
197
+ "additional_requirements": additional_requirements,
198
+ "limit": limit,
199
+ }
200
+
201
+ try:
202
+ # Display the animation while waiting
203
+ with code_container:
204
+ # Create the black box with yellow code animation
205
+ code_display = simulated_code_animation(code_container)
206
+ import time
207
+ strt_time=time.time()
208
+ # Make the API request
209
+ response = requests.post(url, headers=headers, json=payload)
210
+ print(f"total time taken by fast api to generate answer: {time.time()-strt_time}")
211
+ # Clear the code animation
212
+ code_container.empty()
213
+
214
+ if response.status_code == 200:
215
+ # Process successful response
216
+ data = response.json()
217
+ st.success(f"Found {data['count']} matching resumes")
218
+
219
+ # Display each resume with the new card-based layout
220
+ for i, resume in enumerate(data['results']):
221
+ resume_data = resume['resume_data']
222
+ explainability = resume['explainibility']
223
+ # Generate synthetic contact information
224
+ full_name = resume_data.get('full_name', 'No Name')
225
+ # Add synthetic data to resume_data if not present
226
+ if 'email' not in resume_data and 'email_id' not in resume_data:
227
+ resume_data['email'] = generate_email(full_name)
228
+ if 'phone' not in resume_data:
229
+ resume_data['phone'] = generate_phone_number()
230
+ if 'linkedin_profile' not in resume_data and 'linkedin' not in resume_data:
231
+ resume_data['linkedin'] = generate_linkedin(full_name)
232
+
233
+ # Main card with essential information
234
+ with st.container():
235
+ col1, col2 = st.columns([7, 3])
236
+
237
+ with col1:
238
+ # Name with larger font and accent color
239
+ st.markdown(f"""
240
+ <h2 style="color:#2C3E50; font-size:28px; margin-bottom:5px;">
241
+ {full_name}
242
+ </h2>
243
+ """, unsafe_allow_html=True)
244
+
245
+ # Years of experience with an icon
246
+ years_exp = resume_data.get('total_experience_years', 'Not specified')
247
+ st.markdown(f"""
248
+ <div style="margin-bottom:15px;">
249
+ <span style="color:#3299a8; font-size:16px;">
250
+ <i class="fas fa-briefcase"></i> <strong>Years of Experience:</strong>
251
+ </span>
252
+ <span style="font-size:16px;">{years_exp}</span>
253
+ </div>
254
+ """, unsafe_allow_html=True)
255
+
256
+ # Summary in a nice box with a border
257
+ summary = ""
258
+ if 'overall_summary' in resume:
259
+ summary = resume.get('overall_summary')
260
+ elif isinstance(resume_data.get('summary'), str):
261
+ summary = resume_data.get('summary')
262
+
263
+ st.markdown(explainability)
264
+
265
+ if summary:
266
+ st.markdown(f"""
267
+ <div style="border-left:4px solid #3299a8; padding-left:15px;
268
+ background-color:#f8f9fa; padding:10px; border-radius:0 5px 5px 0;">
269
+ <div style="color:#3299a8; font-weight:bold; margin-bottom:5px;">SUMMARY</div>
270
+ <div style="font-size:15px; line-height:1.5;">{summary}</div>
271
+ </div>
272
+ """, unsafe_allow_html=True)
273
+ st.markdown("<br>", unsafe_allow_html=True)
274
+ with col2:
275
+ # Contact card with background
276
+ st.markdown("""
277
+ <div style="background-color:#f0f7fa; border-radius:8px; padding:15px;
278
+ box-shadow:0 2px 5px rgba(0,0,0,0.05); margin-bottom:15px;">
279
+ <h3 style="color:#3299a8; font-size:18px; border-bottom:2px solid #3299a8;
280
+ padding-bottom:8px; margin-top:0;">
281
+ <i class="fas fa-address-card"></i> CONTACT INFORMATION
282
+ </h3>
283
+ """, unsafe_allow_html=True)
284
+
285
+ # Email with icon
286
+ email = resume_data.get('email', resume_data.get('email_id', 'Not provided'))
287
+ st.markdown(f"""
288
+ <div style="margin-bottom:10px;">
289
+ <i class="fas fa-envelope" style="color:#3299a8; width:20px;"></i>
290
+ <strong style="color:#555;">Email:</strong><br>
291
+ <a href="mailto:{email}" style="text-decoration:none; color:#3299a8;
292
+ margin-left:25px; display:block; margin-top:3px;">{email}</a>
293
+ </div>
294
+ """, unsafe_allow_html=True)
295
+
296
+ # Phone with icon
297
+ phone = resume_data.get('phone', 'Not provided')
298
+ st.markdown(f"""
299
+ <div style="margin-bottom:10px;">
300
+ <i class="fas fa-phone" style="color:#3299a8; width:20px;"></i>
301
+ <strong style="color:#555;">Phone:</strong><br>
302
+ <span style="margin-left:25px; display:block; margin-top:3px;">{phone}</span>
303
+ </div>
304
+ """, unsafe_allow_html=True)
305
+
306
+ # Location with icon
307
+ location = resume_data.get('location', 'Not specified')
308
+ st.markdown(f"""
309
+ <div style="margin-bottom:10px;">
310
+ <i class="fas fa-map-marker-alt" style="color:#3299a8; width:20px;"></i>
311
+ <strong style="color:#555;">Location:</strong><br>
312
+ <span style="margin-left:25px; display:block; margin-top:3px;">{location}</span>
313
+ </div>
314
+ """, unsafe_allow_html=True)
315
+
316
+ # LinkedIn with icon
317
+ linkedin_url = resume_data.get('linkedin_profile', resume_data.get('linkedin', 'Not provided'))
318
+ st.markdown(f"""
319
+ <div style="margin-bottom:5px;">
320
+ <i class="fab fa-linkedin" style="color:#3299a8; width:20px;"></i>
321
+ <strong style="color:#555;">LinkedIn:</strong><br>
322
+ <a href="{linkedin_url}" target="_blank" style="text-decoration:none;
323
+ color:#3299a8; margin-left:25px; display:block; margin-top:3px;
324
+ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%;">
325
+ {linkedin_url.replace('https://', '')}
326
+ </a>
327
+ </div>
328
+ </div>
329
+ """, unsafe_allow_html=True)
330
+
331
+ # Create expandable sections for details - MOVED INSIDE THE LOOP
332
+ with st.expander(f"Resume Content for {full_name}", expanded=False):
333
+ tabs = st.tabs(["All Skills", "Experience", "Projects", "Education"])
334
+
335
+ # All Skills tab
336
+ with tabs[0]:
337
+ st.subheader("Skills")
338
+ if 'skills' in resume_data and resume_data['skills']:
339
+ skills_html = ""
340
+ for skill in resume_data['skills']:
341
+ # Check if this skill is in matched_skills
342
+ is_matched = skill in resume.get('matched_skills', [])
343
+ badge_class = "skill-badge" if is_matched else "missing-skill-badge"
344
+ skills_html += f'<span class="{badge_class}">{skill}</span> '
345
+ st.markdown(skills_html, unsafe_allow_html=True)
346
+ else:
347
+ st.write("No skills listed.")
348
+
349
+ # Experiences tab
350
+ with tabs[1]:
351
+ st.subheader("Professional Experience")
352
+ if 'experience' in resume_data and resume_data['experience']:
353
+ for exp in resume_data['experience']:
354
+ with st.container():
355
+ st.markdown(f"""
356
+ <div class="experience-card">
357
+ <strong>{exp.get('title', 'Position')}</strong> at <strong>{exp.get('company', 'Company')}</strong>
358
+ <br><em>{exp.get('duration', '')}</em>
359
+ <p>{exp.get('description', '')}</p>
360
+ </div>
361
+ """, unsafe_allow_html=True)
362
+ elif 'professional_experiences' in resume_data and resume_data['professional_experiences']:
363
+ for exp in resume_data['professional_experiences']:
364
+ with st.container():
365
+ st.markdown(f"""
366
+ <div class="experience-card">
367
+ <strong>{exp.get('title', 'Position')}</strong> at <strong>{exp.get('company', 'Company')}</strong>
368
+ <br><em>{exp.get('duration', '')}</em>
369
+ <p>{exp.get('description', '')}</p>
370
+ </div>
371
+ """, unsafe_allow_html=True)
372
+ else:
373
+ st.write("No experience information available.")
374
+
375
+ # Projects tab
376
+ with tabs[2]:
377
+ st.subheader("Projects")
378
+ if 'projects' in resume_data and resume_data['projects']:
379
+ for project in resume_data['projects']:
380
+ if isinstance(project, dict):
381
+ project_name = project.get('name', project.get('title', 'Project'))
382
+ project_desc = project.get('description', '')
383
+ st.markdown(f"**{project_name}**")
384
+ st.markdown(f"{project_desc}")
385
+ st.markdown("---")
386
+ else:
387
+ st.markdown(f"- {project}")
388
+ else:
389
+ st.write("No projects listed.")
390
+
391
+ # Education tab
392
+ with tabs[3]:
393
+ st.subheader("Education")
394
+ if 'education' in resume_data and resume_data['education']:
395
+ for edu in resume_data['education']:
396
+ with st.container():
397
+ st.markdown(f"""
398
+ <div class="experience-card">
399
+ <strong>{edu.get('degree', 'Degree')}</strong>
400
+ <br><strong>{edu.get('institution', 'Institution')}</strong>
401
+ <br><em>{edu.get('date', '')}</em>
402
+ </div>
403
+ """, unsafe_allow_html=True)
404
+ else:
405
+ st.write("No education information available.")
406
+
407
+
408
+
409
+ st.markdown("""
410
+ <style>
411
+ .download-resume-btn {
412
+ display: inline-block;
413
+ width: 100%;
414
+ background-color: #3299a8;
415
+ color: white;
416
+ text-align: center;
417
+ padding: 12px 20px;
418
+ margin: 8px 0 25px 0;
419
+ border-radius: 5px;
420
+ border: none;
421
+ cursor: pointer;
422
+ font-size: 16px;
423
+ font-weight: 500;
424
+ transition: all 0.3s ease;
425
+ text-decoration: none;
426
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
427
+ }
428
+
429
+ .download-resume-btn:hover {
430
+ background-color: #277a86;
431
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
432
+ transform: translateY(-2px);
433
+ }
434
+
435
+ .download-resume-btn i {
436
+ margin-right: 10px;
437
+ }
438
+ </style>
439
+ """, unsafe_allow_html=True)
440
+
441
+ # Create a centered container for the download button
442
+ col1, col2, col3 = st.columns([1, 2, 1])
443
+ with col2:
444
+ # Render the download button with icon
445
+ st.markdown(f"""
446
+ <button class="download-resume-btn" onclick="alert('This is a demo feature. In a production environment, this would download the resume for {full_name}.')">
447
+ <i class="fas fa-file-download"></i> Download Resume
448
+ </button>
449
+ """, unsafe_allow_html=True)
450
+
451
+ # Add a separator between resumes
452
+ st.markdown("---")
453
+ except Exception as e:
454
+ # Clear the code animation in case of error
455
+ code_container.empty()
456
+ st.error(f"An error occurred: {str(e)}")
457
+
458
+ if __name__ == "__main__":
459
+ main()