Prathmesh0001/interview-system
0
1"""2AI Mock Interview System - Enhanced with Advanced Metrics35 personalized resume-based questions with 40+ evaluation metrics4"""5 6from flask import Flask, render_template, request, jsonify, send_file, redirect, url_for7from flask_cors import CORS8import os9from datetime import datetime10from werkzeug.utils import secure_filename11import uuid12import traceback13 14# Import modules15from document_parser import DocumentParser16from question_generator import QuestionGenerator17from answer_analyzer import AnswerAnalyzer # Now uses EnhancedAnswerAnalyzer18from report_generator import ReportGenerator19 20app = Flask(__name__)21app.secret_key = os.urandom(24)22CORS(app)23 24# Config25UPLOAD_FOLDER = 'uploads'26REPORTS_FOLDER = 'reports'27ALLOWED_EXTENSIONS = {'pdf', 'docx', 'txt'}28 29os.makedirs(UPLOAD_FOLDER, exist_ok=True)30os.makedirs(REPORTS_FOLDER, exist_ok=True)31os.makedirs('templates', exist_ok=True)32os.makedirs('static/css', exist_ok=True)33 34app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER35app.config['REPORTS_FOLDER'] = REPORTS_FOLDER36app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 102437 38# Initialize39doc_parser = DocumentParser()40question_generator = QuestionGenerator()41answer_analyzer = AnswerAnalyzer()42report_generator = ReportGenerator()43 44# Sessions storage45interview_sessions = {}46 47 48def allowed_file(filename):49 return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS50 51 52@app.route('/')53def index():54 """Upload page"""55 print("\n๐ Homepage accessed")56 return render_template('index.html')57 58 59@app.route('/upload', methods=['POST'])60def upload_documents():61 """Handle file upload and question generation"""62 try:63 print("\n๐ค Upload request received")64 65 # Validate files66 if 'resume' not in request.files or 'job_description' not in request.files:67 print("โ Missing files in request")68 return jsonify({'success': False, 'error': 'Both files required'}), 40069 70 resume_file = request.files['resume']71 jd_file = request.files['job_description']72 73 print(f"๐ Resume: {resume_file.filename}")74 print(f"๐ JD: {jd_file.filename}")75 76 if resume_file.filename == '' or jd_file.filename == '':77 return jsonify({'success': False, 'error': 'No file selected'}), 40078 79 if not (allowed_file(resume_file.filename) and allowed_file(jd_file.filename)):80 return jsonify({'success': False, 'error': 'Invalid format'}), 40081 82 # Save files83 resume_path = os.path.join(app.config['UPLOAD_FOLDER'], 84 secure_filename(resume_file.filename))85 jd_path = os.path.join(app.config['UPLOAD_FOLDER'], 86 secure_filename(jd_file.filename))87 88 resume_file.save(resume_path)89 jd_file.save(jd_path)90 print("โ
Files saved")91 92 # Parse93 print("๐ Parsing documents...")94 resume_text = doc_parser.parse_document(resume_path)95 jd_text = doc_parser.parse_document(jd_path)96 97 if not resume_text or not jd_text:98 return jsonify({'success': False, 'error': 'Failed to parse documents'}), 40099 100 print(f"โ
Resume: {len(resume_text)} chars")101 print(f"โ
JD: {len(jd_text)} chars")102 103 # Generate session104 session_id = str(uuid.uuid4())105 print(f"๐ Session: {session_id}")106 107 # Generate questions108 print("๐ฏ Generating 5 resume-based questions...")109 all_questions = question_generator.generate_resume_specific_questions(110 resume_text, jd_text, 5111 )112 113 print(f"โ
Generated {len(all_questions)} questions:")114 for i, q in enumerate(all_questions, 1):115 print(f" {i}. {q['question'][:60]}...")116 117 # Store session118 interview_sessions[session_id] = {119 'resume_text': resume_text,120 'jd_text': jd_text,121 'questions': all_questions,122 'answers': [],123 'current_question': 0,124 'created_at': datetime.now().isoformat()125 }126 127 print(f"๐พ Session stored. Total sessions: {len(interview_sessions)}")128 print(f" Session keys: {list(interview_sessions.keys())}")129 130 return jsonify({131 'success': True,132 'session_id': session_id,133 'total_questions': len(all_questions)134 })135 136 except Exception as e:137 print(f"\nโ Upload error: {e}")138 traceback.print_exc()139 return jsonify({'success': False, 'error': str(e)}), 500140 141 142@app.route('/interview/<session_id>')143def interview_page(session_id):144 """Interview page with video"""145 print(f"\n๐ฅ Interview page requested: {session_id}")146 print(f" Available sessions: {list(interview_sessions.keys())}")147 148 if session_id not in interview_sessions:149 print(f"โ Invalid session: {session_id}")150 return redirect(url_for('index'))151 152 print(f"โ
Session found. Questions: {len(interview_sessions[session_id]['questions'])}")153 return render_template('interview.html', session_id=session_id)154 155 156@app.route('/api/get-question/<session_id>', methods=['GET'])157def get_question(session_id):158 """Get current question"""159 try:160 print(f"\n๐ Question request for session: {session_id}")161 print(f" Available sessions: {list(interview_sessions.keys())}")162 163 if not session_id or session_id == '':164 print("โ Empty session ID")165 return jsonify({'error': 'No session ID provided'}), 400166 167 if session_id not in interview_sessions:168 print(f"โ Invalid session: {session_id}")169 print(f" Session not found in: {list(interview_sessions.keys())}")170 return jsonify({'error': 'Invalid session. Please upload files again.'}), 404171 172 session_data = interview_sessions[session_id]173 current_idx = session_data['current_question']174 questions = session_data['questions']175 176 print(f" Current: {current_idx + 1}/{len(questions)}")177 178 if current_idx >= len(questions):179 print("โ
Interview complete")180 return jsonify({181 'completed': True,182 'message': 'Interview completed'183 })184 185 question = questions[current_idx]186 187 print(f" Question: {question['question'][:50]}...")188 189 response_data = {190 'completed': False,191 'question_number': current_idx + 1,192 'total_questions': len(questions),193 'question': question['question'],194 'category': question.get('category', 'Resume-Based'),195 'difficulty': question.get('difficulty', 'intermediate'),196 'focus_area': question.get('focus_area', '')197 }198 199 print(f" Sending response: {response_data}")200 return jsonify(response_data)201 202 except Exception as e:203 print(f"โ Get question error: {e}")204 traceback.print_exc()205 return jsonify({'error': str(e)}), 500206 207 208@app.route('/api/submit-answer/<session_id>', methods=['POST'])209def submit_answer(session_id):210 """Submit answer and get feedback"""211 try:212 print(f"\nโ๏ธ Answer submission for session: {session_id}")213 214 if session_id not in interview_sessions:215 print(f"โ Invalid session")216 return jsonify({'success': False, 'error': 'Invalid session'}), 404217 218 data = request.get_json()219 answer_text = data.get('answer', '')220 duration = data.get('duration', 0)221 222 print(f" Answer length: {len(answer_text)} chars")223 print(f" Duration: {duration}s")224 225 session_data = interview_sessions[session_id]226 current_idx = session_data['current_question']227 question = session_data['questions'][current_idx]228 229 # ========== ENHANCED METRICS INTEGRATION ==========230 # Analyze answer with ENHANCED metrics231 print("๐ Analyzing answer with enhanced metrics...")232 job_description = session_data.get('jd_text', '')233 234 analysis = answer_analyzer.analyze_answer(235 answer_text,236 question['question'],237 job_description=job_description,238 audio_duration=duration239 )240 # ==================================================241 242 # Generate feedback243 feedback = answer_analyzer.get_feedback(analysis)244 245 # Display enhanced metrics in terminal246 print(f" ๐ Overall Score: {analysis['overall_score']:.1f}")247 if 'composite_scores' in analysis:248 composite = analysis['composite_scores']249 print(f" ๐ Composite Scores:")250 print(f" - Communication: {composite.get('communication_effectiveness_score', 0):.1f}")251 print(f" - Confidence: {composite.get('confidence_score', 0):.1f}")252 print(f" - Professionalism: {composite.get('professionalism_score', 0):.1f}")253 254 if 'content_quality' in analysis:255 print(f" ๐ Content Quality: {analysis['content_quality'].get('quality_score', 0):.1f}")256 257 if 'speech_analysis' in analysis:258 speech = analysis['speech_analysis']259 print(f" ๐ฃ๏ธ Speech Rate: {speech.get('speech_rate_wpm', 0):.0f} WPM")260 print(f" ๐ฌ Filler Words: {speech.get('filler_word_count', 0)}")261 262 # Store answer263 session_data['answers'].append({264 'question': question['question'],265 'answer': answer_text,266 'duration': duration,267 'analysis': analysis,268 'feedback': feedback,269 'timestamp': datetime.now().isoformat()270 })271 272 # Move to next question273 session_data['current_question'] += 1274 is_complete = session_data['current_question'] >= len(session_data['questions'])275 276 print(f" Progress: {session_data['current_question']}/{len(session_data['questions'])}")277 print(f" Complete: {is_complete}")278 279 return jsonify({280 'success': True,281 'score': analysis['overall_score'],282 'feedback': feedback[:3], # Return top 3 feedback points283 'is_complete': is_complete284 })285 286 except Exception as e:287 print(f"โ Submit answer error: {e}")288 traceback.print_exc()289 return jsonify({'success': False, 'error': str(e)}), 500290 291 292@app.route('/results/<session_id>')293def results_page(session_id):294 """Results page"""295 print(f"\n๐ Results page requested: {session_id}")296 297 if session_id not in interview_sessions:298 print(f"โ Invalid session")299 return redirect(url_for('index'))300 301 print(f"โ
Session found")302 return render_template('results.html', session_id=session_id)303 304 305@app.route('/api/generate-report/<session_id>', methods=['POST'])306def generate_report(session_id):307 """Generate final report"""308 try:309 print(f"\n๐ Report generation for session: {session_id}")310 311 if session_id not in interview_sessions:312 return jsonify({'success': False, 'error': 'Invalid session'}), 404313 314 session_data = interview_sessions[session_id]315 316 print("๐ Generating comprehensive report...")317 318 # Prepare interview data for report319 interview_data = {320 'answers': session_data['answers'],321 'resume_text': session_data['resume_text'],322 'jd_text': session_data['jd_text'],323 'timestamp': session_data['created_at']324 }325 326 # Generate report path327 report_filename = f'interview_report_{session_id}.pdf'328 report_path = os.path.join(app.config['REPORTS_FOLDER'], report_filename)329 330 # Generate the report331 success = report_generator.generate_report(interview_data, report_path)332 333 if not success:334 return jsonify({'success': False, 'error': 'Failed to generate report'}), 500335 336 print(f"โ
Report generated: {report_path}")337 338 # Calculate metrics using enhanced analysis339 scores = [ans['analysis']['overall_score'] for ans in session_data['answers']]340 overall_score = sum(scores) / len(scores) if scores else 0341 342 # Extract individual metric scores from enhanced analyzer343 content_scores = [ans['analysis']['content_quality']['quality_score'] for ans in session_data['answers']]344 345 # Try to get composite scores if available (enhanced metrics)346 if 'composite_scores' in session_data['answers'][0]['analysis']:347 clarity_scores = [ans['analysis']['composite_scores']['clarity_score'] for ans in session_data['answers']]348 confidence_scores = [ans['analysis']['composite_scores']['confidence_score'] for ans in session_data['answers']]349 professional_scores = [ans['analysis']['composite_scores']['professionalism_score'] for ans in session_data['answers']]350 else:351 # Fallback to old metrics structure352 clarity_scores = [ans['analysis']['clarity']['clarity_score'] for ans in session_data['answers']]353 confidence_scores = [ans['analysis']['sentiment']['confidence_level'] for ans in session_data['answers']]354 professional_scores = [ans['analysis']['professionalism']['professionalism_score'] for ans in session_data['answers']]355 356 metrics = {357 'content_score': sum(content_scores) / len(content_scores) if content_scores else 70,358 'clarity_score': sum(clarity_scores) / len(clarity_scores) if clarity_scores else 70,359 'confidence_score': sum(confidence_scores) / len(confidence_scores) if confidence_scores else 70,360 'professionalism_score': sum(professional_scores) / len(professional_scores) if professional_scores else 70361 }362 363 print(f" Overall: {overall_score:.1f}")364 print(f" Metrics: {metrics}")365 366 return jsonify({367 'success': True,368 'overall_score': overall_score,369 'metrics': metrics,370 'report_url': f'/download-report/{session_id}'371 })372 373 except Exception as e:374 print(f"โ Report generation error: {e}")375 traceback.print_exc()376 return jsonify({'success': False, 'error': str(e)}), 500377 378 379@app.route('/download-report/<session_id>')380def download_report(session_id):381 """Download PDF report"""382 try:383 report_path = os.path.join(app.config['REPORTS_FOLDER'], f'interview_report_{session_id}.pdf')384 385 if not os.path.exists(report_path):386 return "Report not found", 404387 388 return send_file(389 report_path,390 as_attachment=True,391 download_name=f'interview_report_{datetime.now().strftime("%Y%m%d")}.pdf'392 )393 except Exception as e:394 print(f"โ Download error: {e}")395 return str(e), 500396 397 398# Debug route to check sessions399@app.route('/api/debug/sessions')400def debug_sessions():401 """Debug endpoint to check active sessions"""402 return jsonify({403 'total_sessions': len(interview_sessions),404 'session_ids': list(interview_sessions.keys()),405 'sessions': {406 sid: {407 'questions': len(data['questions']),408 'answers': len(data['answers']),409 'current': data['current_question'],410 'created': data['created_at']411 }412 for sid, data in interview_sessions.items()413 }414 })415 416 417if __name__ == '__main__':418 print("\n" + "="*70)419 print("๐ AI MOCK INTERVIEW - ENHANCED METRICS SYSTEM")420 print("="*70)421 print(f"๐ Upload folder: {UPLOAD_FOLDER}")422 print(f"๐ Reports folder: {REPORTS_FOLDER}")423 print(f"๐ Access at: http://localhost:5000")424 print("\nโจ Features:")425 print(" โข 5 personalized resume-based questions")426 print(" โข 40+ advanced evaluation metrics")427 print(" โข Real-time comprehensive feedback")428 print(" โข Detailed PDF performance reports")429 print("="*70 + "\n")430 431 app.run(host="0.0.0.0", port=7860)