rak-301 commited on
Commit
98fae1f
·
verified ·
1 Parent(s): e25b197

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -80
app.py CHANGED
@@ -3,67 +3,46 @@ import asyncio
3
  import nest_asyncio
4
  import streamlit as st
5
  import time
6
- from PyPDF2 import PdfReader
7
  from pdfminer.high_level import extract_text
8
  from docx import Document
9
  import os
10
- from openai import OpenAI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
 
13
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
14
-
15
- prompt = """
16
-
17
- You are tasked with creating a detailed and specific 10-Day Interview Preparation Guide for a user based on their resume and a provided job description. This guide should be tailored to prepare the user for an interview for the given job role. The guide should cover both technical and soft skills, with a focus on specific topics relevant to the job description. The preparation plan should balance revising concepts already known to the user and addressing any gaps in their knowledge.
18
-
19
- Instructions:
20
-
21
- Your responses for Day 1 and Day 10 will be fixed and as follows:
22
-
23
- Day 1: Research & Initial Study
24
-
25
- Your response for Day 1 should include -
26
-
27
- Researching the company, its mission, and its products or services. Also, study the job description thoroughly to understand the key responsibilities and required skills. And finally, note down any unfamiliar terms or technologies for further research.
28
-
29
- Day 10: Review and Final Preparation
30
-
31
- Your response for Day 1 should include -
32
-
33
- A review all the topics covered in the previous days.
34
-
35
- Focus on the soft skills required for the role.
36
-
37
- Conducting a final mock interview, including a variety of question types (behavioral, technical, situational, etc.), to ensure thorough preparation.
38
-
39
- The remaining days (ie, days 2-9) will address specific aspects of the preparation that are most needed for the job and should include :
40
-
41
- - For each day, provide a detailed plan focusing on specific technical skills, concepts, or tools that are relevant to the job role.
42
-
43
- - Include concepts that align with both the user's existing knowledge (as indicated by their resume) and new areas that are essential according to the job description.
44
-
45
- - Ensure there is a balance between reinforcing known skills and addressing knowledge gaps.
46
-
47
- - Have each day or a group of day focus on a specific aspect or group of skills required for the job
48
-
49
- Additional Requirements:
50
-
51
- If there isn't enough content to cover over 10 individual days, combine multiple days to cover a single concepts in order to avoid repetition across different days
52
-
53
- Within the points for each day avoid further subheadings
54
-
55
- Speak to the user - "Your Resume..."
56
-
57
- Simply begin by saying - "Here is your 10 Day Interview Prep Guide:"
58
-
59
- The guide should be specific, with each day's tasks clearly outlined and explained.
60
-
61
- The steps for each day should be detailed, with technical depth and practical advice.
62
-
63
- Ensure the guide is practical and actionable, helping the user prepare effectively for their interview.
64
-
65
- """
66
-
67
  def extract_text_from_pdf(file):
68
  try:
69
  # Reset file pointer to the start of the file
@@ -92,33 +71,105 @@ def extract_text_from_file(file):
92
  return "Unsupported file type"
93
 
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
- def call_openai_api(job_description, resume_text):
97
- client = OpenAI(api_key =OPENAI_API_KEY)
98
 
99
- completion = client.chat.completions.create(
100
- model="gpt-4o",
101
- temperature=0.1,
102
- messages=[
103
- {"role": "system", "content": prompt},
104
- {"role": "user", "content": f"Here is the resume text: {resume_text} \n Here is the job description: {job_description}"}
105
- ]
106
- )
107
-
108
- return completion.choices[0].message.content
109
 
110
 
111
- def call_api(resume_text, job_description):
112
- task = call_openai_api(job_description, resume_text)
113
- return task
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
 
116
  st.set_page_config(page_title="Resume Analyzer", layout="centered")
117
-
118
  st.title("Resume Analyzer")
119
 
120
  job_description_source = st.radio("Job Description Source", ("Upload File", "Paste Text"))
121
-
122
  if job_description_source == "Upload File":
123
  job_description_file = st.file_uploader("Upload Job Description", type=['pdf', 'docx', 'txt'])
124
  job_description = extract_text_from_file(job_description_file) if job_description_file else None
@@ -131,22 +182,48 @@ if job_description:
131
  st.subheader("Job Description Preview")
132
  st.text_area("Job Description", job_description, height=200)
133
 
134
- resume_text = None
135
  if resume_file:
136
  resume_text = extract_text_from_file(resume_file)
137
  st.subheader("Resume Preview")
138
  st.text_area("Resume", resume_text, height=200)
139
 
 
 
 
 
 
 
 
140
 
141
  if st.button("Analyze Resume"):
142
  if job_description and resume_file:
143
  start_time = time.time()
144
- responses_async = call_api(resume_text, job_description)
145
  end_time = time.time()
146
  total_time = end_time - start_time
147
- st.subheader("Analysis Result")
148
- st.text_area("Result", responses_async, height=400)
149
- # st.write(f"Time taken for OpenAI response: {response_time:.2f} seconds")
150
- st.write(f"Total time taken for execution: {total_time:.2f} seconds")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  else:
152
- st.error("Please provide both the job description and resume.")
 
3
  import nest_asyncio
4
  import streamlit as st
5
  import time
 
6
  from pdfminer.high_level import extract_text
7
  from docx import Document
8
  import os
9
+ from pdf2image import convert_from_bytes
10
+ import base64
11
+ import io
12
+ import certifi
13
+ import ssl
14
+ # from dotenv import load_dotenv
15
+ import json
16
+ import testing
17
+ from prompts import (
18
+ ANALYSIS_PROMPT,
19
+ ATS_PROMPT,
20
+ OBJECTIVE_ANALYSIS_PROMPT,
21
+ SCORING_PROMPT,
22
+ DEFUALT_PREP_GUIDE,
23
+ LOW_PREP_GUIDE
24
+ )
25
+
26
+ # load_dotenv()
27
+ api_key = os.getenv('OPENAI_API_KEY')
28
+
29
+ nest_asyncio.apply()
30
+
31
+ def generate_prompts(num_images):
32
+ return {
33
+ "SCORING": SCORING_PROMPT,
34
+ "ANALYSIS": ANALYSIS_PROMPT,
35
+ "OBJECTIVE_ANALYSIS": OBJECTIVE_ANALYSIS_PROMPT,
36
+ "ATS": ATS_PROMPT.format(pages=num_images),
37
+ }
38
+
39
+
40
+ def encode_image(image):
41
+ buffered = io.BytesIO()
42
+ image.save(buffered, format="PNG")
43
+ return base64.b64encode(buffered.getvalue()).decode('utf-8')
44
 
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  def extract_text_from_pdf(file):
47
  try:
48
  # Reset file pointer to the start of the file
 
71
  return "Unsupported file type"
72
 
73
 
74
+ async def generate_completion(message, api_key):
75
+ url = "https://api.openai.com/v1/chat/completions"
76
+ headers = {
77
+ "Content-Type": "application/json",
78
+ "Authorization": f"Bearer {api_key}"
79
+ }
80
+
81
+ payload = {
82
+ "model": "gpt-4o",
83
+ "messages": message,
84
+ "temperature": 0.1,
85
+ "response_format": {"type": "json_object"}
86
+ }
87
+
88
+ ssl_context = ssl.create_default_context(cafile=certifi.where())
89
+ # Use certifi to specify the CA bundle for SSL certificate verification
90
+ async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=ssl_context)) as session:
91
+ async with session.post(url, headers=headers, json=payload) as response:
92
+ if response.status != 200:
93
+ print(f"Error: {response.status}")
94
+ return None
95
+ response_json = await response.json()
96
+ return response_json
97
+
98
+ async def main(resume_text, job_description):
99
+
100
+ analysis_types_and_prompts = {
101
+ "SCORING": SCORING_PROMPT,
102
+ "ANALYSIS": ANALYSIS_PROMPT,
103
+ "OBJECTIVE_ANALYSIS": OBJECTIVE_ANALYSIS_PROMPT,
104
+ "ATS": ATS_PROMPT.format(pages=pdf_length)
105
+ }
106
+
107
+ tasks = []
108
+ for analysis, prompt in analysis_types_and_prompts.items():
109
+ messages = construct_message(
110
+ analysis,
111
+ prompt,
112
+ resume_text,
113
+ job_description,
114
+ base_image if analysis == "ATS" else None,
115
+ )
116
+ tasks.append(generate_completion(messages,api_key))
117
+
118
+ scoring_task = asyncio.create_task(tasks[0])
119
+
120
+ other_tasks = tasks[1:]
121
+ other_tasks_futures = [asyncio.create_task(task) for task in other_tasks]
122
+
123
+ result_scoring = await scoring_task
124
+ scoring_json = json.loads(result_scoring['choices'][0]['message']['content'])
125
+ if len(scoring_json) == 1:
126
+ return scoring_json, None # Error dictionary
127
+
128
+ other_results = await asyncio.gather(*other_tasks_futures)
129
+ responses_async = [result_scoring] + other_results
130
+
131
+ if scoring_json['Overall']['Score'] <=50:
132
+ guide_prompt = LOW_PREP_GUIDE
133
+ else:
134
+ guide_prompt = DEFUALT_PREP_GUIDE
135
 
136
+ return responses_async, guide_prompt
 
137
 
 
 
 
 
 
 
 
 
 
 
138
 
139
 
140
+ def construct_message(analysis, prompt, resume_text, job_description=None, base_image=None):
141
+ if analysis == "ATS":
142
+ messages = [
143
+ {"role": "system", "content": prompt},
144
+ {"role": "user",
145
+ "content": f"Here is the Job Description: {job_description}"},
146
+ {"role": "user", "content": []}
147
+ ]
148
+ for image in base_image:
149
+ messages[2]["content"].append({
150
+ "type": "image_url",
151
+ "image_url": {
152
+ "url": f"data:image/png;base64,{image}"
153
+ },
154
+ })
155
+ return messages
156
+
157
+ elif analysis == "OBJECTIVE_ANALYSIS":
158
+ return ([
159
+ {"role": "system", "content": prompt},
160
+ {"role": "user",
161
+ "content": f"Here is the resume text: {resume_text}"}])
162
+ else:
163
+ return ([
164
+ {"role": "system", "content": prompt},
165
+ {"role": "user",
166
+ "content": f"Here is the resume text: {resume_text} \n Here is the job description: {job_description}"}])
167
 
168
 
169
  st.set_page_config(page_title="Resume Analyzer", layout="centered")
 
170
  st.title("Resume Analyzer")
171
 
172
  job_description_source = st.radio("Job Description Source", ("Upload File", "Paste Text"))
 
173
  if job_description_source == "Upload File":
174
  job_description_file = st.file_uploader("Upload Job Description", type=['pdf', 'docx', 'txt'])
175
  job_description = extract_text_from_file(job_description_file) if job_description_file else None
 
182
  st.subheader("Job Description Preview")
183
  st.text_area("Job Description", job_description, height=200)
184
 
 
185
  if resume_file:
186
  resume_text = extract_text_from_file(resume_file)
187
  st.subheader("Resume Preview")
188
  st.text_area("Resume", resume_text, height=200)
189
 
190
+ resume_file.seek(0)
191
+ file_bytes = resume_file.read()
192
+ images = convert_from_bytes(file_bytes)
193
+ pdf_length = len(images)
194
+ base_image = []
195
+ for image in images:
196
+ base_image.append(encode_image(image))
197
 
198
  if st.button("Analyze Resume"):
199
  if job_description and resume_file:
200
  start_time = time.time()
201
+ responses_async, guide_prompt = asyncio.run(main(resume_text, job_description))
202
  end_time = time.time()
203
  total_time = end_time - start_time
204
+
205
+ guide_message = construct_message("GUIDE", guide_prompt, resume_text, job_description)
206
+ guide_response = asyncio.run(generate_completion(guide_message, api_key))
207
+
208
+
209
+ if isinstance(responses_async, dict) and "Error" in responses_async:
210
+ error_message = f"Improper file entered: {responses_async['Error']}"
211
+ st.write(error_message)
212
+
213
+ else:
214
+ st.subheader("Analysis Result")
215
+ st.text_area("Scoring Analysis", responses_async[0]['choices'][0]['message']['content'], height=400)
216
+ st.text_area("Gap Analysis", responses_async[1]['choices'][0]['message']['content'], height=400)
217
+ st.text_area("Objective Analysis", responses_async[2]['choices'][0]['message']['content'], height=400)
218
+ st.text_area("ATS Analysis", responses_async[3]['choices'][0]['message']['content'], height=400)
219
+ st.text_area("Interview Prep Guide", guide_response['choices'][0]['message']['content'], height=400)
220
+ st.write(f"Image prompt input tokens: {responses_async[2]['usage']['prompt_tokens']}")
221
+ # st.write(f"Time taken for OpenAI response: {response_time:.2f} seconds")
222
+ st.write(f"Total time taken for execution: {total_time:.2f} seconds")
223
+
224
+ #Testing JSON Respones:
225
+ # testing.run_tests(responses_async, guide_response)
226
+
227
+
228
  else:
229
+ st.error("Please provide both the job description and resume.")