Prathmesh0001/interview-system
0
1"""
2AI Mock Interview System - Enhanced with Advanced Metrics
35 personalized resume-based questions with 40+ evaluation metrics
4"""
5
6from flask import Flask, render_template, request, jsonify, send_file, redirect, url_for
7from flask_cors import CORS
8import os
9from datetime import datetime
10from werkzeug.utils import secure_filename
11import uuid
12import traceback
13
14# Import modules
15from document_parser import DocumentParser
16from question_generator import QuestionGenerator
17from answer_analyzer import AnswerAnalyzer # Now uses EnhancedAnswerAnalyzer
18from report_generator import ReportGenerator
19
20app = Flask(__name__)
21app.secret_key = os.urandom(24)
22CORS(app)
23
24# Config
25UPLOAD_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_FOLDER
35app.config['REPORTS_FOLDER'] = REPORTS_FOLDER
36app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
37
38# Initialize
39doc_parser = DocumentParser()
40question_generator = QuestionGenerator()
41answer_analyzer = AnswerAnalyzer()
42report_generator = ReportGenerator()
43
44# Sessions storage
45interview_sessions = {}
46
47
48def allowed_file(filename):
49 return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
50
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 files
66 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'}), 400
69
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'}), 400
78
79 if not (allowed_file(resume_file.filename) and allowed_file(jd_file.filename)):
80 return jsonify({'success': False, 'error': 'Invalid format'}), 400
81
82 # Save files
83 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 # Parse
93 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'}), 400
99
100 print(f"โ
Resume: {len(resume_text)} chars")
101 print(f"โ
JD: {len(jd_text)} chars")
102
103 # Generate session
104 session_id = str(uuid.uuid4())
105 print(f"๐ Session: {session_id}")
106
107 # Generate questions
108 print("๐ฏ Generating 5 resume-based questions...")
109 all_questions = question_generator.generate_resume_specific_questions(
110 resume_text, jd_text, 5
111 )
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 session
118 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)}), 500
140
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'}), 400
166
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.'}), 404
171
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)}), 500
206
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'}), 404
217
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 metrics
231 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=duration
239 )
240 # ==================================================
241
242 # Generate feedback
243 feedback = answer_analyzer.get_feedback(analysis)
244
245 # Display enhanced metrics in terminal
246 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 answer
263 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 question
273 session_data['current_question'] += 1
274 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 points
283 'is_complete': is_complete
284 })
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)}), 500
290
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'}), 404
313
314 session_data = interview_sessions[session_id]
315
316 print("๐ Generating comprehensive report...")
317
318 # Prepare interview data for report
319 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 path
327 report_filename = f'interview_report_{session_id}.pdf'
328 report_path = os.path.join(app.config['REPORTS_FOLDER'], report_filename)
329
330 # Generate the report
331 success = report_generator.generate_report(interview_data, report_path)
332
333 if not success:
334 return jsonify({'success': False, 'error': 'Failed to generate report'}), 500
335
336 print(f"โ
Report generated: {report_path}")
337
338 # Calculate metrics using enhanced analysis
339 scores = [ans['analysis']['overall_score'] for ans in session_data['answers']]
340 overall_score = sum(scores) / len(scores) if scores else 0
341
342 # Extract individual metric scores from enhanced analyzer
343 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 structure
352 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 70
361 }
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)}), 500
377
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", 404
387
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), 500
396
397
398# Debug route to check sessions
399@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(debug=True, host='0.0.0.0', port=5000)