Mangesh223 commited on
Commit
3027706
·
verified ·
1 Parent(s): b27339a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -118
app.py CHANGED
@@ -5,143 +5,81 @@ import docx
5
  import requests
6
  import json
7
 
8
- # Function to extract text from PDF (unchanged)
9
  def extract_text_from_pdf(file):
10
  pdf_reader = PyPDF2.PdfReader(file)
11
- text = ""
12
- for page in pdf_reader.pages:
13
- text += page.extract_text()
14
- return text
15
 
16
- # Function to extract text from Word document (unchanged)
17
  def extract_text_from_docx(file):
18
  doc = docx.Document(file)
19
- text = "\n".join([para.text for para in doc.paragraphs])
20
- return text
21
 
22
- # Function to process uploaded file (only added case-insensitive check)
23
  def process_uploaded_file(file):
24
- filename = file.name.lower() # Case-insensitive check
25
- if filename.endswith(".pdf"):
26
  return extract_text_from_pdf(file)
27
- elif filename.endswith(".docx"):
28
  return extract_text_from_docx(file)
29
- else:
30
- raise ValueError("Unsupported file format. Please upload a PDF or Word document.")
31
 
32
- # Function to call Together API (unchanged except for timeout)
33
-
34
-
35
- def analyze_with_huggingface(resume_text, job_description):
36
- HF_API_KEY = os.getenv("HUGGINGFACE_API_KEY")
37
- API_URL = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.3"
38
-
39
- # Truncate inputs to stay within token limits
40
- resume_text = resume_text[:3000] # Limit resume to 3000 chars
41
- job_description = job_description[:1000] # Limit JD to 1000 chars
42
-
43
- prompt = f"""<s>[INST] <<SYS>>
44
- You are an AI expert in ATS resume analysis. Return ONLY valid JSON matching this schema:
45
- {{
46
- "ATS Parameters": {{
47
- "Keywords": {{"Match": <int>, "Recommendation": [<str>]}},
48
- "Formatting": {{"Match": <int>, "Recommendation": [<str>]}},
49
- "Skills Match": {{"Match": <int>, "Recommendation": [<str>]}},
50
- "Experience Relevance": {{"Match": <int>, "Recommendation": [<str>]}},
51
- "Education": {{"Match": <int>, "Recommendation": [<str>]}}
52
- }},
53
- "Score": {{
54
- "Keywords": <int>,
55
- "Formatting": <int>,
56
- "Skills Match": <int>,
57
- "Experience Relevance": <int>,
58
- "Education": <int>,
59
- "Overall": <int>
60
- }}
61
- }}
62
- <</SYS>>
63
-
64
- Analyze this resume against the job description for ATS compatibility:
65
- Resume: {resume_text}
66
- Job Description: {job_description}
67
- Return ONLY the JSON output with no additional text or explanations.[/INST]"""
68
-
69
- headers = {
70
- "Authorization": f"Bearer {HF_API_KEY}",
71
- "Content-Type": "application/json",
72
- }
73
-
74
- payload = {
75
- "inputs": prompt,
76
- "parameters": {
77
- "max_new_tokens": 800,
78
- "temperature": 0.7,
79
- "do_sample": True,
80
- "return_full_text": False
81
- }
82
- }
83
-
84
  try:
85
- response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
86
- response.raise_for_status()
87
-
88
- # Extract JSON from response
89
- result = response.json()
90
- if isinstance(result, list):
91
- content = result[0].get("generated_text", "{}")
92
- else:
93
- content = result.get("generated_text", "{}")
94
-
95
- # Clean the output (sometimes models add extra text)
96
- json_start = content.find('{')
97
- json_end = content.rfind('}') + 1
98
- json_str = content[json_start:json_end]
99
-
100
- parsed = json.loads(json_str)
101
- if not all(k in parsed for k in ["ATS Parameters", "Score"]):
102
- raise ValueError("Invalid JSON structure")
103
-
104
- return parsed
105
 
106
- except Exception as e:
107
- return {"error": f"Hugging Face API error: {str(e)}"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- # Main function (only added JSON validation)
110
- def analyze_resume(file, job_description):
111
- try:
112
- resume_text = process_uploaded_file(file)
113
- result = analyze_with_mistral(resume_text, job_description)
 
 
 
 
 
 
 
 
114
 
115
- # Ensure the output matches your original format
116
- if isinstance(result, dict) and "error" in result:
117
- return json.dumps(result, indent=2)
118
- return result
119
 
120
  except Exception as e:
121
- return json.dumps({"error": str(e)}, indent=2)
122
 
123
- # Gradio interface (only changed file component type)
124
- with gr.Blocks(fill_height=True, title="Smart ATS Resume Analyzer") as demo:
125
- with gr.Sidebar():
126
- gr.Markdown("# Smart ATS Resume Analyzer")
127
- gr.Markdown("Upload your resume (PDF/Word) and enter a job description to get an ATS compatibility score.")
128
-
129
  with gr.Row():
130
- with gr.Column(scale=1):
131
- resume_upload = gr.File(
132
- label="Upload Resume (PDF or Word)",
133
- file_types=[".pdf", ".docx"],
134
- type="filepath" # Only this line changed
135
- )
136
- job_desc = gr.Textbox(label="Job Description", lines=10, placeholder="Paste the job description here...")
137
- submit_btn = gr.Button("Analyze Resume")
138
- with gr.Column(scale=2):
139
- output = gr.JSON(label="ATS Analysis Result")
140
 
141
- submit_btn.click(
142
- fn=analyze_resume,
143
- inputs=[resume_upload, job_desc],
144
  outputs=output
145
  )
146
 
147
- demo.launch()
 
5
  import requests
6
  import json
7
 
8
+ # Text extraction functions (unchanged)
9
  def extract_text_from_pdf(file):
10
  pdf_reader = PyPDF2.PdfReader(file)
11
+ return " ".join(page.extract_text() for page in pdf_reader.pages)
 
 
 
12
 
 
13
  def extract_text_from_docx(file):
14
  doc = docx.Document(file)
15
+ return "\n".join(para.text for para in doc.paragraphs)
 
16
 
 
17
  def process_uploaded_file(file):
18
+ if file.name.lower().endswith(".pdf"):
 
19
  return extract_text_from_pdf(file)
20
+ elif file.name.lower().endswith(".docx"):
21
  return extract_text_from_docx(file)
22
+ raise ValueError("Unsupported file format")
 
23
 
24
+ # Updated Hugging Face API function
25
+ def analyze_with_huggingface(file, job_description):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  try:
27
+ resume_text = process_uploaded_file(file)
28
+ HF_API_KEY = os.getenv("HUGGINGFACE_API_KEY")
29
+ API_URL = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.3"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ prompt = """<s>[INST] <<SYS>>
32
+ You are an ATS resume analyzer. Return ONLY this JSON format:
33
+ {
34
+ "ATS Parameters": {
35
+ "Keywords": {"Match": 0-100, "Recommendation": []},
36
+ "Formatting": {"Match": 0-100, "Recommendation": []},
37
+ "Skills": {"Match": 0-100, "Recommendation": []},
38
+ "Experience": {"Match": 0-100, "Recommendation": []},
39
+ "Education": {"Match": 0-100, "Recommendation": []}
40
+ },
41
+ "Score": {
42
+ "Overall": 0-100,
43
+ "Details": {"Keywords": 0-100, "Formatting": 0-100, "Skills": 0-100, "Experience": 0-100, "Education": 0-100}
44
+ }
45
+ }
46
+ <</SYS>>
47
 
48
+ Resume: {resume}
49
+ Job Description: {jd}
50
+ [/INST]""".format(resume=resume_text[:3000], jd=job_description[:1000])
51
+
52
+ response = requests.post(
53
+ API_URL,
54
+ headers={"Authorization": f"Bearer {HF_API_KEY}"},
55
+ json={
56
+ "inputs": prompt,
57
+ "parameters": {"max_new_tokens": 800, "temperature": 0.7}
58
+ },
59
+ timeout=30
60
+ )
61
 
62
+ # Extract JSON from response
63
+ result = response.json()[0]['generated_text']
64
+ json_str = result[result.find('{'):result.rfind('}')+1]
65
+ return json.loads(json_str)
66
 
67
  except Exception as e:
68
+ return {"error": str(e)}
69
 
70
+ # Gradio Interface
71
+ with gr.Blocks() as demo:
72
+ gr.Markdown("## Resume Analyzer")
 
 
 
73
  with gr.Row():
74
+ file_input = gr.File(label="Upload Resume", file_types=[".pdf", ".docx"])
75
+ jd_input = gr.Textbox(label="Job Description", lines=10)
76
+ analyze_btn = gr.Button("Analyze")
77
+ output = gr.JSON()
 
 
 
 
 
 
78
 
79
+ analyze_btn.click(
80
+ fn=analyze_with_huggingface,
81
+ inputs=[file_input, jd_input],
82
  outputs=output
83
  )
84
 
85
+ demo.queue().launch(allowed_paths=["*"])