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")