Resume_score / final_app.py
Bhanuprasadchouki's picture
Update final_app.py
126b611 verified
import re
import google.generativeai as genai
import PyPDF2
import streamlit as st
from docx import Document
from langchain.memory import ConversationBufferMemory
from langchain.prompts import (ChatPromptTemplate, HumanMessagePromptTemplate,
MessagesPlaceholder)
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnableLambda, RunnablePassthrough
from langchain_core.messages import SystemMessage # Updated import
from langchain_google_genai import ChatGoogleGenerativeAI
# Function to extract text from PDF
def extract_text_from_pdf(uploaded_file):
text = ""
reader = PyPDF2.PdfReader(uploaded_file)
for page in reader.pages:
text += page.extract_text()
return text
# Function to extract text from Word document
def extract_text_from_word(uploaded_file):
text = ""
doc = Document(uploaded_file)
for paragraph in doc.paragraphs:
text += paragraph.text + "\n"
return text
def parse_mcq_questions(mcq_list):
# Split the string into individual questions
questions = re.split(r'\d+\.\s+', mcq_list)[1:] # Skip the empty first element
parsed_questions = []
for q in questions:
# Split into question and options
parts = q.strip().split(' - ')
question = parts[0].strip()
options = {
opt[0]: opt[2:].strip()
for opt in parts[1:]
}
parsed_questions.append({
'question': question,
'options': options
})
return parsed_questions
# Function to generate MCQs using LLM
def generate_mcqs(keywords):
# Construct the query
query = {"human_input": f"""
You are an advanced AI model trained to generate high-quality multiple-choice questions (MCQs).
Based on the provided list of skills: {keywords}, create **exactly 10 MCQs**. Each MCQ should focus on most important concepts related to the internal topics of each skill.
For example, if the keyword is "Python," the questions should be derived from core Python concepts, like data structures, syntax, or libraries.
The MCQs should follow this structure:
1. A clear and concise important question based on a topic within the skill.
2. Four options (labeled as A, B, C, and D).
3. Only one correct answer per question, with the other options serving as plausible distractors.
Do not provide any other information, explanations, or extra text. Output **only** the 10 MCQs in proper structure, like this:
1. Question text...
- A) Option 1
- B) Option 2
- C) Option 3
- D) Option 4
2. Question text...
- A) Option 1
- B) Option 2
- C) Option 3
- D) Option 4
Continue this format for all 10 questions.
"""}
# Invoke the language model to generate MCQs
response = chain.invoke(query)
memory.save_context(query, {"output": response})
# Return the generated MCQs as a string
return response
# Function to evaluate MCQ answers
def evaluate_mcqs(mcq_list, answers):
query = {"human_input": f"""
You are an advanced AI model trained to evaluate answers for high-quality multiple-choice questions (MCQs). Act as an expert professional in all relevant skills and concepts, analyzing the user's answers in detail. Follow these instructions:
1. Evaluate the provided answers {answers} against the correct answers for the MCQs.
2. Award 1 mark for each correct answer. Determine if each answer is correct or incorrect.
3. For incorrect answers:
- Analyze deeply to identify the specific concepts or subtopics within the skill where the user is struggling.
- Provide a focused list of concepts the user needs to improve on, derived from the incorrect answers.
4. At the end of the evaluation, output:
- Total marks scored (out of 10).
- A detailed and analyzed one by one list of concepts to focus on, ensuring they address the root areas of misunderstanding or lack of knowledge.
Output **only** the following information:
- Total marks scored: X/10
- Concepts to focus on: [Provide an analyzed and specific list of concepts derived from incorrect answers]
"""}
response = chain.invoke(query)
memory.save_context(query, {"output": response})
return response
# Function to generate Questions using LLM
def generate_questions(keywords):
# Construct the query
query = {"human_input": f"""
You are a highly advanced AI trained to act as a real-time interview expert. Based on the provided keywords {keywords}, identify the most relevant skills and generate exactly two coding interview questions.
These questions should adhere to the professional structure used in coding platforms like LeetCode or HackerRank. Follow these instructions:
1. Analyze the provided keywords to identify key skills and concepts.
2. Generate two easy to medium-level coding questions that align with these skills.
3. Ensure the questions are well-structured, with a clear problem statement, input format, output format, and example(s) for clarity.
4. Output the questions in the following format:
Question 1: [Title of the Question]
Problem Statement: [Provide a clear description of the problem.]
Input Format: [Specify the format of input(s).]
Output Format: [Specify the format of output(s).]
Constraints: [Mention constraints, if applicable.]
Example(s):
- Input: [Provide sample input]
- Output: [Provide corresponding output]
Question 2: [Title of the Question]
Problem Statement: [Provide a clear description of the problem.]
Input Format: [Specify the format of input(s).]
Output Format: [Specify the format of output(s).]
Constraints: [Mention constraints, if applicable.]
Example(s):
- Input: [Provide sample input]
- Output: [Provide corresponding output]
"""}
# Invoke the language model to generate MCQs
response = chain.invoke(query)
memory.save_context(query, {"output": response})
# Return the generated MCQs as a string
return response
# Function to Interview start using LLM
def interview(job_description_keywords):
# Construct the query
query = {"human_input": f"""
You are a real-time expert interviewer with in-depth knowledge of various industries, job roles, and market trends.
Your task is to conduct an interview for a specific job role based on the given keywords: {job_description_keywords}.
Analyze the keywords to fully understand the role's responsibilities, required skills, and challenges. Use this understanding to ask relevant and impactful interview questions.
Rules:
1. Begin the interview with a self-introduction question to ease the candidate into the process.
2. Ask 10 highly effective, real-world interview questions tailored to the role, progressing from general to more specific and challenging.
3. Ensure the questions focus on assessing the candidate’s practical knowledge, problem-solving skills, and ability to handle real-world scenarios.
4. Incorporate situational and behavioral questions to evaluate how the candidate handles challenges and decision-making.
5. The last two questions must delve into the candidate’s past projects, focusing on:
- The project's purpose and goals.
- Challenges faced and how they were addressed.
- Impact and measurable outcomes.
6. Provide one question at a time, without additional context, explanations, or formatting.
7. Questions must be clear, concise, and aligned with the job role, ensuring they reflect real-time industry expectations.
Start the interview with the first question.
"""}
# Invoke the language model to generate MCQs
response = chain.invoke(query)
memory.save_context(query, {"output": response})
# Return the generated MCQs as a string
return response
# Initialize Google Generative AI chat model
def initialize_chat_model():
with open("key.txt", "r") as f:
GOOGLE_API_KEY = f.read().strip()
chat_model = ChatGoogleGenerativeAI(
google_api_key=GOOGLE_API_KEY,
model="gemini-1.5-pro-latest",
temperature=0.4,
max_tokens=2000,
timeout=120,
max_retries=5,
top_p=0.9,
top_k=40,
presence_penalty=0.6,
frequency_penalty=0.3
)
return chat_model
chat_model = initialize_chat_model()
# Create Chat Template
chat_prompt_template = ChatPromptTemplate.from_messages(
[
SystemMessage(
content=""" You are a language model designed to follow user instructions exactly as given.
Do not take any actions or provide any information unless specifically directed by the user.
Your role is to fulfill the user's requests precisely without deviating from the instructions provided."""
),
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{human_input}")
]
)
# Initialize the Memory
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# Create an Output Parser
output_parser = StrOutputParser()
# Define a chain
chain = RunnablePassthrough.assign(
chat_history=RunnableLambda(lambda human_input: memory.load_memory_variables(human_input)['chat_history'])
) | chat_prompt_template | chat_model | output_parser
# Streamlit App
st.title("Interview Preparation with AI")
st.markdown("## Part-1: Upload Files, Summarize, and Extract Keywords")
# File upload section
file1 = st.file_uploader("Upload your resume (PDF or DOCX):", type=["pdf", "docx"])
file2 = st.file_uploader("Upload the job description (PDF or DOCX):", type=["pdf", "docx"])
if file1 and file2:
try:
# Detect file type and extract text for file 1
if file1.name.endswith('.pdf'):
text1 = extract_text_from_pdf(file1)
elif file1.name.endswith('.docx'):
text1 = extract_text_from_word(file1)
else:
st.error("Unsupported file type for file 1")
# Detect file type and extract text for file 2
if file2.name.endswith('.pdf'):
text2 = extract_text_from_pdf(file2)
elif file2.name.endswith('.docx'):
text2 = extract_text_from_word(file2)
else:
st.error("Unsupported file type for file 2")
# Ensure session state variables are initialized
# if "ats_score_calculated" not in st.session_state:
# st.session_state.ats_score_calculated = False
if 'resume_keywords' not in st.session_state:
st.session_state.resume_keywords = text1
if 'job_description_keywords' not in st.session_state:
st.session_state.job_description_keywords = text2
# Button to Calculate ATS Score
if st.button("ATS Score"): #or st.session_state.ats_score_calculated:
#st.session_state.ats_score_calculated = True
st.markdown("### ATS Score Calculation")
query = {"human_input": f"""
Act as an expert ATS (Applicant Tracking System) analyst with 15+ years of experience in HR tech.
Perform deep analysis of this resume and job description with military precision:
Job Description: {job_description_keywords}
Resume: {resume_keywords}
Execute these steps systematically:
1. INITIAL ANALYSIS PHASE
a. Extract ALL requirements from job description with priority levels (mandatory/nice-to-have)
b. Parse resume with entity recognition (skills, dates, roles, certifications)
c. Create skill ontology mapping between job requirements and resume content
2. REQUIREMENTS BREAKDOWN ENGINE
For EACH job requirement:
i. Check exact match in resume - calculate total duration across positions
ii. If no direct match:
- Split requirement into 3-5 sub-skills using competency framework
- For each sub-skill:
* Search related terms in resume
* Calculate cumulative experience duration
* Apply 30% experience decay for indirect matches
iii. Apply scoring:
- 0.5 pts per year of direct experience (max 4pts/requirement)
- 0.3 pts per year of indirect sub-skill experience (max 2pts/sub-skill)
- -1.5 pts for missing mandatory requirements
3. EXPERIENCE CALCULATION MATRIX
a. Parse employment dates with month/year precision
b. For overlapping positions: apply parallel experience weighting (x1.2)
c. Convert all experience to decimal years (e.g., 1y 6m = 1.5)
d. For management roles: add 0.5y virtual experience per subordinate
4. SECTION OPTIMIZATION SCORECARD
Analyze and score (0-100) with weighted impact:
- Technical Skills Match (35% weight)
- Experience Duration (25%)
- Education/Certifications (15%)
- Keyword Density (10%)
- Project Relevance (10%)
- Leadership Keywords (5%)
5. REAL-TIME ATS SCORE REPORT
Generate structured output with:
A. Requirement Analysis Matrix:
| Requirement | Type | Direct Exp | Sub-Skills Matched | Sub-Skill Exp | Score | Hit/Miss |
B. Experience Calculation Ledger:
| Skill Cluster | Resume Terms | Positions Matched | Raw Duration | Weighted Duration |
C. Gap Analysis:
- Top 3 missing requirements
- Under-qualified areas with improvement roadmap
D. Final ATS Score Breakdown:
- Base Score (0-100)
- Bonus Points (certifications, premium education)
- Penalties (missing mandatory)
- Final Adjusted Score
E. Optimization Recommendations:
- Exact terminology to add
- Strategic placement suggestions
- Skill emphasis ratios
F. Give the final overal ATS score
FORMAT REQUIREMENTS:
- Use markdown tables with exact duration calculations
- Show intermediate scoring computations
- Highlight critical matches/misses with icons (✅/⚠️/❌)
- Include confidence intervals for experience calculations
- Add time-adjusted scoring (older experience = 0.8 decay/year)
- Normalize scores against industry benchmarks
Deliver professional-grade analysis suitable for enterprise HR decisions.
"""}
response = chain.invoke(query)
memory.save_context(query, {"output": response})
st.write(response)
if 'questions' not in st.session_state:
# Your MCQ string goes here
mcq_list = generate_mcqs(st.session_state.job_description_keywords)
st.session_state.questions = parse_mcq_questions(mcq_list)
if 'current_question' not in st.session_state:
st.session_state.current_question = 0
if 'answers' not in st.session_state:
st.session_state.answers = []
if "mcq_button" not in st.session_state:
st.session_state.mcq_button = False
if st.button("MCQ Test") or st.session_state.mcq_button:
st.session_state.mcq_button = True
# Display current question number and total questions
st.write(f"Question {st.session_state.current_question + 1} of {len(st.session_state.questions)}")
# Display current question
current_q = st.session_state.questions[st.session_state.current_question]
st.write(current_q['question'])
# Create radio buttons for options with the corrected format_func
answer = st.radio(
"Select your answer:",
options=['A', 'B', 'C', 'D'], # List of option keys
format_func=lambda x: f"{x}) {current_q['options'].get(x, ' ')}",
key=f"question_{st.session_state.current_question}" # Unique key per question
)
# Navigation buttons in columns
col1, col2 = st.columns(2)
if st.session_state.current_question > 0:
with col1:
if st.button("Previous"):
st.session_state.current_question -= 1
st.rerun()
if st.session_state.current_question < len(st.session_state.questions) - 1:
with col2:
if st.button("Next"):
st.session_state.answers.append(f"{st.session_state.current_question + 1}-{answer}")
st.session_state.current_question += 1
st.rerun()
else:
with col2:
if st.button("Submit"):
st.session_state.answers.append(f"{st.session_state.current_question + 1}-{answer}")
st.write("Quiz completed! Your answers:")
query = {"human_input": f"""
You are an advanced AI model trained to evaluate answers for high-quality multiple-choice questions (MCQs). Act as an expert professional in all relevant skills and concepts, analyzing the user's answers in detail. Follow these instructions:
1. Evaluate the provided answers : {st.session_state.answers} against the correct answers for the MCQs.
2. Award 1 mark for each correct answer. Determine if each answer is correct or incorrect.
3. For incorrect answers:
- Analyze deeply to identify the specific concepts or subtopics within the skill where the user is struggling.
- Provide a focused list of concepts the user needs to improve on, derived from the incorrect answers.
4. At the end of the evaluation, output:
- Total marks scored (out of 10).
- A detailed and analyzed one by one list of concepts to focus on, ensuring they address the root areas of misunderstanding or lack of knowledge.
Output **only** the following information:
- Total marks scored: X/10
- Concepts to focus on: [Provide an analyzed and specific list of concepts derived from incorrect answers]
"""}
response = chain.invoke(query)
memory.save_context(query, {"output": response})
st.session_state.mcq_button = False
#st.write(response)
#st.write(st.session_state.answers)
if "generate_questions_button" not in st.session_state:
st.session_state.generate_questions_button = False
if st.button("Generate Questions") or st.session_state.generate_questions_button:
st.session_state.generate_questions_button = True
# Generate questions
if 'questions_response' not in st.session_state:
st.session_state.questions_response = generate_questions(st.session_state.job_description_keywords)
# Split questions
code_questions = [q.strip() for q in st.session_state.questions_response.split("Question")[1:]]
code_questions = [f"Question{q}" for q in code_questions]
# Display questions and collect answers
st.session_state.code_questions = code_questions
st.session_state.coding_answers = [""] * len(code_questions)
# Display each question with a text area for answers
for i, question in enumerate(code_questions):
st.markdown(f"### {question}")
cod_answer = st.text_area(f"Your Answer for Question {i+1}", key=f"answer_{i}")
st.session_state.coding_answers[i] = cod_answer
if st.button("Submit Answers"):
st.write("### Submitted Answers:")
#st.write(st.session_state.coding_answers)
query = {"human_input": f"""
Evaluate the following user responses to two coding questions:
**User Responses:**
{st.session_state.coding_answers}
**Evaluation Criteria:**
* **Perfection:** Each question carries 10 marks.
* **Assess:**
* Correctness of the code logic and implementation.
* Efficiency of the solution (time and space complexity).
* Code readability, maintainability, and adherence to best practices.
* Handling of edge cases and potential errors.
**Output:**
* **Marks:**
* **Question 1:** [Out of 10 marks]
* **Question 2:** [Out of 10 marks]
* **Analysis:**
* Identify areas where the user needs to improve.
* Suggest specific topics or concepts for further study and practice.
* Provide constructive feedback on the user's approach and coding style.
**Note:**
* Provide a concise and informative evaluation.
* Avoid vague or generic feedback.
"""}
response = chain.invoke(query)
memory.save_context(query, {"output": response})
st.session_state.generate_questions_button = False
st.write(response)
if "Interview_questions_button" not in st.session_state:
st.session_state.Interview_questions_button = False
if st.button("Interview Questions") or st.session_state.Interview_questions_button:
st.session_state.Interview_questions_button = True
if 'flag' not in st.session_state:
st.session_state.flag = 1
if st.session_state.flag <= 10 :
if 'interview_questions' not in st.session_state:
st.session_state.interview_questions = interview(st.session_state.job_description_keywords)
st.write(st.session_state.interview_questions)
#Input from the user using chat_input
human_prompt = st.chat_input(" Message Pega ...")
if True :
query = {"human_input": "I am Bhanu prasad, I have completed my graduation in 2024"}
query1 = {"human_input": human_prompt}
st.write(query)
st.write(query1)
response = chain.invoke(query1)
memory.save_context(query, {"output": response})
st.write(response)
st.session_state.flag += 1
except Exception as e:
st.error(f"An error occurred: {e}")
else:
st.info("Please upload both files to proceed.")