Karthikeyan
commited on
Commit
·
cb65bf0
1
Parent(s):
d1840d5
Upload 3 files
Browse files- app.py +145 -0
- requirements.txt +5 -0
- style.css +36 -0
app.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import PyPDF2
|
| 3 |
+
import os
|
| 4 |
+
import openai
|
| 5 |
+
import re
|
| 6 |
+
import plotly.graph_objects as go
|
| 7 |
+
|
| 8 |
+
class ResumeAnalyser:
|
| 9 |
+
def __init__(self):
|
| 10 |
+
pass
|
| 11 |
+
def extract_text_from_file(self,file_path):
|
| 12 |
+
# Get the file extension
|
| 13 |
+
file_extension = os.path.splitext(file_path)[1]
|
| 14 |
+
|
| 15 |
+
if file_extension == '.pdf':
|
| 16 |
+
with open(file_path, 'rb') as file:
|
| 17 |
+
# Create a PDF file reader object
|
| 18 |
+
reader = PyPDF2.PdfFileReader(file)
|
| 19 |
+
|
| 20 |
+
# Create an empty string to hold the extracted text
|
| 21 |
+
extracted_text = ""
|
| 22 |
+
|
| 23 |
+
# Loop through each page in the PDF and extract the text
|
| 24 |
+
for page_number in range(reader.getNumPages()):
|
| 25 |
+
page = reader.getPage(page_number)
|
| 26 |
+
extracted_text += page.extractText()
|
| 27 |
+
return extracted_text
|
| 28 |
+
|
| 29 |
+
elif file_extension == '.txt':
|
| 30 |
+
with open(file_path, 'r') as file:
|
| 31 |
+
# Just read the entire contents of the text file
|
| 32 |
+
return file.read()
|
| 33 |
+
|
| 34 |
+
else:
|
| 35 |
+
return "Unsupported file type"
|
| 36 |
+
|
| 37 |
+
def responce_from_ai(self,textjd, textcv):
|
| 38 |
+
resume = self.extract_text_from_file(textjd)
|
| 39 |
+
job_description = self.extract_text_from_file(textcv)
|
| 40 |
+
|
| 41 |
+
response = openai.Completion.create(
|
| 42 |
+
engine="text-davinci-003",
|
| 43 |
+
prompt=f"""
|
| 44 |
+
Given the job description and the resume, assess the matching percentage to 100 and if 100 percentage not matched mention the remaining percentage with reason. **Job Description:**{job_description}**Resume:**{resume}
|
| 45 |
+
**Detailed Analysis:**
|
| 46 |
+
the result should be in this format:
|
| 47 |
+
Matched Percentage: [matching percentage].
|
| 48 |
+
Reason : [Mention Reason and keys from job_description and resume get this matched percentage.].
|
| 49 |
+
Skills To Improve : [Mention the skills How to improve and get 100 percentage job description matching].
|
| 50 |
+
Keywords : [matched key words from {job_description} and {resume}].
|
| 51 |
+
""",
|
| 52 |
+
temperature=0,
|
| 53 |
+
max_tokens=100,
|
| 54 |
+
n=1,
|
| 55 |
+
stop=None,
|
| 56 |
+
)
|
| 57 |
+
generated_text = response.choices[0].text.strip()
|
| 58 |
+
print(generated_text)
|
| 59 |
+
return generated_text
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def matching_percentage(self,job_description_path, resume_path):
|
| 63 |
+
job_description_path = job_description_path.name
|
| 64 |
+
resume_path = resume_path.name
|
| 65 |
+
|
| 66 |
+
generated_text = self.responce_from_ai(job_description_path, resume_path)
|
| 67 |
+
|
| 68 |
+
result = generated_text
|
| 69 |
+
|
| 70 |
+
lines = result.split('\n')
|
| 71 |
+
|
| 72 |
+
matched_percentage = None
|
| 73 |
+
matched_percentage_txt = None
|
| 74 |
+
reason = None
|
| 75 |
+
skills_to_improve = None
|
| 76 |
+
keywords = None
|
| 77 |
+
|
| 78 |
+
for line in lines:
|
| 79 |
+
if line.startswith('Matched Percentage:'):
|
| 80 |
+
match = re.search(r"Matched Percentage: (\d+)%", line)
|
| 81 |
+
if match:
|
| 82 |
+
matched_percentage = int(match.group(1))
|
| 83 |
+
matched_percentage_txt = (f"Matched Percentage: {matched_percentage}%")
|
| 84 |
+
elif line.startswith('Reason'):
|
| 85 |
+
reason = line.split(':')[1].strip()
|
| 86 |
+
elif line.startswith('Skills To Improve'):
|
| 87 |
+
skills_to_improve = line.split(':')[1].strip()
|
| 88 |
+
elif line.startswith('Keywords'):
|
| 89 |
+
keywords = line.split(':')[1].strip()
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
# Extract the matched percentage using regular expression
|
| 93 |
+
# match1 = re.search(r"Matched Percentage: (\d+)%", matched_percentage)
|
| 94 |
+
# matched_Percentage = int(match1.group(1))
|
| 95 |
+
|
| 96 |
+
# Creating a pie chart with plotly
|
| 97 |
+
labels = ['Matched', 'Remaining']
|
| 98 |
+
values = [matched_percentage, 100 - matched_percentage]
|
| 99 |
+
|
| 100 |
+
fig = go.Figure(data=[go.Pie(labels=labels, values=values)])
|
| 101 |
+
# fig.update_layout(title='Matched Percentage')
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
return matched_percentage_txt,reason, skills_to_improve, keywords,fig
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def gradio_interface(self):
|
| 108 |
+
with gr.Blocks(css="style.css",theme=gr.themes.Soft()) as app:
|
| 109 |
+
gr.HTML("""<img class="leftimage" align="left" src="https://companieslogo.com/img/orig/RAND.AS_BIG-0f1935a4.png?t=1651813778" alt="Image" width="210" height="210">
|
| 110 |
+
<img class="rightimage" align="right" src="https://workllama.com/wp-content/uploads/2022/05/WL_Logo.svg" alt="Image" width="210" height="210">""")
|
| 111 |
+
|
| 112 |
+
with gr.Row():
|
| 113 |
+
with gr.Column(elem_id="col-container"):
|
| 114 |
+
gr.HTML(
|
| 115 |
+
"""<br style="color:white;">"""
|
| 116 |
+
)
|
| 117 |
+
gr.HTML(
|
| 118 |
+
"""<h2 style="text-align:center; color:"white">WorkLLama Resume Matcher</h2> """
|
| 119 |
+
)
|
| 120 |
+
gr.HTML("<br>")
|
| 121 |
+
with gr.Row():
|
| 122 |
+
with gr.Column(scale=0.45, min_width=150, ):
|
| 123 |
+
jobDescription = gr.File(label="Job Description")
|
| 124 |
+
with gr.Column(scale=0.45, min_width=150):
|
| 125 |
+
resume = gr.File(label="Resume")
|
| 126 |
+
with gr.Column(scale=0.10, min_width=150):
|
| 127 |
+
analyse = gr.Button("Analyse")
|
| 128 |
+
with gr.Row():
|
| 129 |
+
with gr.Column(scale=1.0, min_width=150):
|
| 130 |
+
perncentage = gr.Textbox(label="Matching Percentage",lines=8)
|
| 131 |
+
with gr.Column(scale=1.0, min_width=150):
|
| 132 |
+
reason = gr.Textbox(label="Matching Reason",lines=8)
|
| 133 |
+
with gr.Column(scale=1.0, min_width=150):
|
| 134 |
+
skills = gr.Textbox(label="Skills To Improve",lines=8)
|
| 135 |
+
with gr.Column(scale=1.0, min_width=150):
|
| 136 |
+
keywords = gr.Textbox(label="Matched Keywords",lines=8)
|
| 137 |
+
with gr.Row():
|
| 138 |
+
with gr.Column(scale=1.0, min_width=150):
|
| 139 |
+
pychart = gr.Plot(label="Matching Percentage Chart")
|
| 140 |
+
analyse.click(self.matching_percentage, [jobDescription, resume], [perncentage,reason,skills,keywords,pychart])
|
| 141 |
+
|
| 142 |
+
app.launch()
|
| 143 |
+
|
| 144 |
+
resume=ResumeAnalyser()
|
| 145 |
+
resume.gradio_interface()
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
openai
|
| 2 |
+
gradio
|
| 3 |
+
PyPDF2==2.12.1
|
| 4 |
+
plotly
|
| 5 |
+
plotly-express
|
style.css
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#col-container {
|
| 2 |
+
max-width: 600px;
|
| 3 |
+
margin-left: auto;
|
| 4 |
+
margin-right: auto;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
#row-flex {
|
| 8 |
+
display: flex;
|
| 9 |
+
align-items: center;
|
| 10 |
+
justify-content: center;
|
| 11 |
+
}
|
| 12 |
+
.leftimage .rightimage{
|
| 13 |
+
float:left;
|
| 14 |
+
filter: drop-shadow(20px 20px 10px white);
|
| 15 |
+
}
|
| 16 |
+
.leftimage{
|
| 17 |
+
padding-top:40px;
|
| 18 |
+
margin-left:310px;
|
| 19 |
+
}
|
| 20 |
+
.rightimage{
|
| 21 |
+
padding-top:40px;
|
| 22 |
+
margin-right:320px;
|
| 23 |
+
}
|
| 24 |
+
a,
|
| 25 |
+
a:hover,
|
| 26 |
+
a:visited {
|
| 27 |
+
text-decoration-line: underline;
|
| 28 |
+
font-weight: 600;
|
| 29 |
+
color: #1f2937 !important;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
.dark a,
|
| 33 |
+
.dark a:hover,
|
| 34 |
+
.dark a:visited {
|
| 35 |
+
color: #f3f4f6 !important;
|
| 36 |
+
}
|