Imarticuslearning's picture
Update app.py
0d3e7f5 verified
import os
import re
import time
import asyncio
import tempfile
import streamlit as st
import google.generativeai as genai
import edge_tts
import speech_recognition as sr
from dotenv import load_dotenv
import pandas as pd
# βœ… Streamlit page config
st.set_page_config(page_title="GrillMaster", layout="wide")
# Load API key
load_dotenv()
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
# -----------------------------
# SESSION STATE DEFAULTS
# -----------------------------
defaults = {
"generated_questions": [],
"current_question_index": 0,
"answers": [],
"evaluations": [],
"evaluation_feedback": "",
"overall_score": 0,
"percentage_score": 0,
"is_recording": False,
"question_played": False,
"selected_domain": "",
"response_captured": False,
"timer_start": None,
"show_intro": False,
"recorded_text": "",
"recording_complete": False,
"recording_started": False,
"audio_played": False,
"question_start_time": 0.0,
"record_phase": "",
"improvement_suggestions_generated": False,
"improvement_suggestions": ""
}
for key, value in defaults.items():
if key not in st.session_state:
st.session_state[key] = value
# -----------------------------
# QUESTIONS
# -----------------------------
CANDIDATE_QUESTIONS = [
{"text": "Can you introduce yourself?", "type": "introduction"},
{"text": "Why do you want to be a part of Analytics domain?", "type": "introduction"},
{"text": "Can you try to explain any project of yours in detail?", "type": "project"},
{"text": "Any challenges faced while working on the project?", "type": "project"},
{"text": "What could be the business impact of the project?", "type": "project"}
]
# -----------------------------
# AUDIO GENERATION
# -----------------------------
async def generate_question_audio(question, voice="en-IE-EmilyNeural"):
clean_question = re.sub(r'[^A-Za-z0-9.,?! ]+', '', question)
tts = edge_tts.Communicate(text=clean_question, voice=voice)
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
await tts.save(tmp_file.name)
return tmp_file.name
# -----------------------------
# EVALUATION FUNCTION
# -----------------------------
def evaluate_answer(question_text, answer_text, q_type):
model = genai.GenerativeModel("gemini-2.5-pro")
if q_type == "introduction":
prompt = f"""
You are an expert interviewer evaluating a candidate's introduction. Assess the response based on:
- Clarity & Fluency
- Confidence & Professionalism
- Relevance & Structure
- Conciseness
Provide an evaluation summary with a score out of 10.
Candidate Introduction:
{answer_text}
"""
else: # project explanation
prompt = f"""
You are an expert interviewer evaluating a candidate's project explanation. Assess the response based on:
- Technical Understanding
- Communication Clarity
- Problem-Solving & Impact
- Use of Examples
- Logical Flow & Structure
Provide an evaluation summary with a score out of 10.
Candidate Project Explanation:
{answer_text}
"""
response = model.generate_content(prompt)
text = response.text.strip()
score_match = re.search(r"\*\*Overall Score:\*\* (\d+)/10", text)
score = int(score_match.group(1)) if score_match else 0
return {"score": score, "feedback": text}
# -----------------------------
# IMPROVEMENT SUGGESTIONS
# -----------------------------
def generate_improvement_suggestions():
model = genai.GenerativeModel('gemini-2.5-pro')
if not st.session_state.get("answers"):
st.session_state.improvement_suggestions = "No answers were recorded to generate improvement suggestions."
return
qa_context = []
for i, entry in enumerate(st.session_state["answers"]):
qa_context.append(
f"Question {i+1}: {entry['question']}\nCandidate's Answer {i+1}: {entry.get('response','[No response]')}"
)
full_qa_context = "\n\n".join(qa_context)
prompt = f"""
You are an interview coach. Based on these Q&A:
{full_qa_context}
Provide detailed improvement suggestions for each answer. Be constructive and supportive.
"""
try:
st.info("πŸ€– Generating detailed improvement suggestions...")
response = model.generate_content(prompt)
st.session_state.improvement_suggestions = response.text.strip()
st.session_state.improvement_suggestions_generated = True
st.success("Detailed suggestions generated!")
except Exception as e:
st.error(f"Error generating suggestions: {e}")
st.session_state.improvement_suggestions_generated = False
# -----------------------------
# START PAGE: Candidate Intro Button
# -----------------------------
if not st.session_state["show_intro"]:
st.title("πŸ”₯🎯 Welcome to GrillMaster Mock Interview")
st.markdown("Click the button below to start the Candidate Introduction + Project mock interview:")
if st.button("🎀 Candidate Intro + Project"):
st.session_state.update({
"show_intro": True,
"selected_domain": "Candidate Intro + Project",
"current_question_index": 0,
"answers": [],
"evaluations": [],
"question_played": False
})
st.rerun()
# -----------------------------
# CANDIDATE INTRO WORKFLOW
# -----------------------------
if st.session_state.get("show_intro"):
st.header("🎯 Candidate Introduction + Project")
q_index = st.session_state["current_question_index"]
if q_index < len(CANDIDATE_QUESTIONS):
question = CANDIDATE_QUESTIONS[q_index]
st.subheader(f"Q{q_index+1}: {question['text']}")
# Generate audio
if not st.session_state["question_played"]:
audio_file = asyncio.run(generate_question_audio(question["text"]))
st.audio(audio_file, format="audio/mp3")
st.session_state["question_played"] = True
# Record answer
audio_data = st.audio_input("🎀 Record your answer here")
if audio_data:
audio_bytes = audio_data.read()
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f:
f.write(audio_bytes)
wav_path = f.name
recognizer = sr.Recognizer()
with sr.AudioFile(wav_path) as source:
recorded_audio = recognizer.record(source)
try:
response_text = recognizer.recognize_google(recorded_audio)
st.session_state["current_response"] = response_text
st.success("βœ… Answer recorded. You can re-record or move to next question.")
except sr.UnknownValueError:
st.error("⚠️ Could not understand audio.")
# Buttons
col1, col2 = st.columns(2)
with col1:
if st.button("πŸ”„ Re-record Answer"):
st.session_state.pop("current_response", None)
st.session_state["question_played"] = False
st.experimental_rerun()
with col2:
if "current_response" in st.session_state and st.button("➑️ Next Question"):
st.session_state["answers"].append({
"question": question["text"],
"response": st.session_state.pop("current_response"),
"type": question["type"]
})
st.session_state["question_played"] = False
st.session_state["current_question_index"] += 1
st.rerun()
else:
# Evaluate all answers
if not st.session_state["evaluations"]:
for ans in st.session_state["answers"]:
ev = evaluate_answer(ans["question"], ans["response"], ans["type"])
st.session_state["evaluations"].append(ev)
st.subheader("πŸ“Š Mock Interview Completed")
total_score = sum([ev["score"] for ev in st.session_state["evaluations"]])
overall_score = round(total_score / len(st.session_state["evaluations"]), 2)
st.write(f"**Overall Average Score:** {overall_score}/10")
st.progress(overall_score / 10)
# Show answers & feedback
for i, ans in enumerate(st.session_state["answers"]):
ev = st.session_state["evaluations"][i]
st.write(f"**Q{i+1}: {ans['question']}**")
st.write(f"**A:** {ans['response']}")
st.write(f"**Score:** {ev['score']}/10")
st.write(ev["feedback"])
st.write("---")
# Improvement suggestions
if st.button("πŸ’‘ Generate Improvement Suggestions"):
generate_improvement_suggestions()
st.rerun()
if st.session_state.get("improvement_suggestions_generated"):
with st.expander("πŸ” Improvement Suggestions", expanded=True):
st.markdown(st.session_state["improvement_suggestions"])
# Download summary
def prepare_summary():
text = "# GrillMaster Candidate Intro Summary\n\n"
for i, ans in enumerate(st.session_state["answers"]):
ev = st.session_state["evaluations"][i]
text += f"**Q{i+1}: {ans['question']}**\n**A:** {ans['response']}\n**Score:** {ev['score']}/10\n\n"
if st.session_state.get("improvement_suggestions_generated"):
text += "## Improvement Suggestions:\n" + st.session_state["improvement_suggestions"]
return text.encode("utf-8")
st.download_button("πŸ’Ύ Download Summary", data=prepare_summary(),
file_name=f"GrillMaster_Summary_{time.strftime('%Y%m%d_%H%M')}.md",
mime="text/markdown")