|
|
|
|
|
import warnings |
|
|
warnings.filterwarnings('ignore') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import fitz |
|
|
import docx |
|
|
from crewai_tools import SerperDevTool |
|
|
import os |
|
|
from crewai import Agent, Task, Crew |
|
|
from dotenv import load_dotenv |
|
|
import tempfile |
|
|
|
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
|
|
|
import streamlit as st |
|
|
|
|
|
|
|
|
st.set_page_config(page_title="Smart Resume Analyzer", layout="wide") |
|
|
|
|
|
|
|
|
st.markdown( |
|
|
f""" |
|
|
<style> |
|
|
.stApp {{ |
|
|
background-color: #50a7c7; |
|
|
}} |
|
|
.title-text {{ |
|
|
font-size: 1000px; /* Increased font size */ |
|
|
font-weight: bold; |
|
|
color: white; |
|
|
text-align: center; |
|
|
margin-bottom: 20px; |
|
|
}} |
|
|
.subtitle-text {{ |
|
|
font-style: italic; |
|
|
color: white; |
|
|
text-align: center; |
|
|
margin-bottom: 30px; |
|
|
}} |
|
|
.upload-box {{ |
|
|
border: 2px dashed #ffffff; |
|
|
padding: 20px; |
|
|
text-align: center; |
|
|
background-color: rgba(255, 255, 255, 0.2); |
|
|
border-radius: 10px; |
|
|
margin: 0 auto; |
|
|
width: 50%; /* Reduced width of the upload box */ |
|
|
}} |
|
|
.stTextInput input {{ |
|
|
max-width: 300px; /* Reduced width of the text input */ |
|
|
}} |
|
|
</style> |
|
|
""", |
|
|
unsafe_allow_html=True |
|
|
) |
|
|
|
|
|
|
|
|
st.markdown('<p class="title-text">Smart Resume Analyzer & Job Matcher</p>', unsafe_allow_html=True) |
|
|
st.markdown('<p class="subtitle-text">Expected Runtime: 1 Min</p>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.markdown("### Upload Your Resume (PDF or DOCX)") |
|
|
uploaded_file = st.file_uploader("Drop File Here or Click to Upload", type=["pdf", "docx"], help="Upload your resume for analysis.",key="resume_upload") |
|
|
|
|
|
|
|
|
preferred_location = st.text_input("Preferred Location", placeholder="e.g., San Francisco",max_chars=50, key="location_input") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
submit_button = st.button("Submit",icon="😃") |
|
|
|
|
|
|
|
|
def extract_text_from_pdf(file_path): |
|
|
"""Extracts text from a PDF file using PyMuPDF.""" |
|
|
doc = fitz.open(file_path) |
|
|
text = "" |
|
|
for page in doc: |
|
|
text += page.get_text() |
|
|
return text |
|
|
|
|
|
|
|
|
def extract_text_from_docx(file_path): |
|
|
"""Extracts text from a DOCX file using python-docx.""" |
|
|
doc = docx.Document(file_path) |
|
|
fullText = [] |
|
|
for para in doc.paragraphs: |
|
|
fullText.append(para.text) |
|
|
return "\n".join(fullText) |
|
|
|
|
|
|
|
|
def extract_text_from_resume(file_path): |
|
|
"""Determines file type and extracts text.""" |
|
|
if file_path.endswith(".pdf"): |
|
|
return extract_text_from_pdf(file_path) |
|
|
elif file_path.endswith(".docx"): |
|
|
return extract_text_from_docx(file_path) |
|
|
else: |
|
|
return "Unsupported file format." |
|
|
|
|
|
|
|
|
def initialize_crew(): |
|
|
search_tool = SerperDevTool() |
|
|
|
|
|
|
|
|
resume_advisor = Agent( |
|
|
role="Professional Resume Advisor", |
|
|
goal="Give feedback on the resume to make it stand out in the job market.", |
|
|
verbose=True, |
|
|
backstory="With a strategic mind and an eye for detail, you excel at providing feedback on resumes to highlight the most relevant skills and experiences." |
|
|
) |
|
|
|
|
|
|
|
|
resume_writer = Agent( |
|
|
role="Professional Resume Writer", |
|
|
goal="Based on the feedback received from Resume Advisor, make changes to the resume to make it stand out in the job market.", |
|
|
verbose=True, |
|
|
backstory="With a strategic mind and an eye for detail, you excel at refining resumes based on the feedback to highlight the most relevant skills and experiences." |
|
|
) |
|
|
|
|
|
|
|
|
job_researcher = Agent( |
|
|
role="Senior Recruitment Consultant", |
|
|
goal="Find the 5 most relevant, recently posted jobs based on the improved resume received from resume advisor and the location preference.", |
|
|
tools=[search_tool], |
|
|
verbose=True, |
|
|
backstory="""As a senior recruitment consultant, your prowess in finding the most relevant jobs based on the resume and location preference is unmatched. |
|
|
You can scan the resume efficiently, identify the most suitable job roles, and search for the best-suited recently posted open job positions at the preferred location.""" |
|
|
) |
|
|
|
|
|
|
|
|
resume_advisor_task = Task( |
|
|
description=( |
|
|
"""Give feedback on the resume to make it stand out for recruiters. |
|
|
Review every section, including the summary, work experience, skills, and education. Suggest to add relevant sections if they are missing. |
|
|
Also, give an overall score to the resume out of 10. This is the resume: {resume}""" |
|
|
), |
|
|
expected_output="The overall score of the resume followed by the feedback in bullet points.", |
|
|
agent=resume_advisor |
|
|
) |
|
|
|
|
|
|
|
|
resume_writer_task = Task( |
|
|
description=( |
|
|
"""Rewrite the resume based on the feedback to make it stand out for recruiters. You can adjust and enhance the resume but don't make up facts. |
|
|
Review and update every section, including the summary, work experience, skills, and education to better reflect the candidate's abilities. This is the resume: {resume}""" |
|
|
), |
|
|
expected_output="Resume in markdown format that effectively highlights the candidate's qualifications and experiences.", |
|
|
context=[resume_advisor_task], |
|
|
agent=resume_writer |
|
|
) |
|
|
|
|
|
|
|
|
research_task = Task( |
|
|
description="""Find the 5 most relevant recent job postings based on the resume received from resume advisor and location preference. This is the preferred location: {location}. |
|
|
Use the tools to gather relevant content and shortlist the 5 most relevant, recent, job openings.""", |
|
|
expected_output="A bullet point list of the 5 job openings, with the appropriate links and detailed description about each job, in markdown format.", |
|
|
agent=job_researcher |
|
|
) |
|
|
|
|
|
|
|
|
crew = Crew( |
|
|
name="Resume Analysis Crew", |
|
|
tasks=[resume_advisor_task, resume_writer_task, research_task], |
|
|
agents=[resume_advisor, resume_writer, job_researcher] |
|
|
) |
|
|
|
|
|
return crew,resume_advisor_task, resume_writer_task, research_task |
|
|
|
|
|
|
|
|
|
|
|
def resume_agent(file_path, location): |
|
|
resume_text = extract_text_from_resume(file_path) |
|
|
|
|
|
crew, resume_advisor_task, resume_writer_task, research_task = initialize_crew() |
|
|
result = crew.kickoff(inputs={"resume": resume_text, "location": location}) |
|
|
|
|
|
|
|
|
feedback = resume_advisor_task.output.raw.strip("```markdown").strip("```").strip() |
|
|
improved_resume = resume_writer_task.output.raw.strip("```markdown").strip("```").strip() |
|
|
job_roles = research_task.output.raw.strip("```markdown").strip("```").strip() |
|
|
|
|
|
return feedback, improved_resume, job_roles |
|
|
|
|
|
|
|
|
if submit_button: |
|
|
if uploaded_file is None: |
|
|
st.error("Please upload a resume before submitting.") |
|
|
else: |
|
|
with st.spinner("Analyzing your resume and finding job matches..."): |
|
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(uploaded_file.name)[1]) as tmp_file: |
|
|
tmp_file.write(uploaded_file.getvalue()) |
|
|
tmp_file_path = tmp_file.name |
|
|
|
|
|
try: |
|
|
feedback, improved_resume, job_roles = resume_agent(tmp_file_path, preferred_location) |
|
|
|
|
|
st.success("Analysis complete!") |
|
|
st.markdown("## Resume Feedback:") |
|
|
st.markdown(feedback) |
|
|
|
|
|
st.markdown("## Improved Resume Suggestions:") |
|
|
st.markdown(improved_resume) |
|
|
|
|
|
st.markdown("## Relevant Job Roles:") |
|
|
st.markdown(job_roles) |
|
|
except Exception as e: |
|
|
st.error(f"An error occurred: {e}") |
|
|
finally: |
|
|
|
|
|
os.unlink(tmp_file_path) |