Team Ai
Modelpublic

Prathmesh0001/interview-system

sourceHugging Facemitupdated 8mo agoView on Hugging Face
0likes
app_fixed.py431 linesDownload Raw Back to root
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)