Prathmesh0001/interview-system
0
1"""
2Report Generator Module
3Generates comprehensive interview performance reports in PDF format
4"""
5
6from fpdf import FPDF
7from datetime import datetime
8from typing import Dict, List
9import matplotlib.pyplot as plt
10import io
11import os
12
13
14class InterviewReport(FPDF):
15 """Custom PDF report for interview analysis"""
16
17 def __init__(self):
18 super().__init__()
19 self.set_auto_page_break(auto=True, margin=15)
20
21 def header(self):
22 """Page header"""
23 self.set_font('Arial', 'B', 16)
24 self.cell(0, 10, 'AI Mock Interview - Performance Report', 0, 1, 'C')
25 self.ln(5)
26
27 def footer(self):
28 """Page footer"""
29 self.set_y(-15)
30 self.set_font('Arial', 'I', 8)
31 self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
32
33 def chapter_title(self, title: str):
34 """Add chapter title"""
35 self.set_font('Arial', 'B', 14)
36 self.set_fill_color(52, 152, 219)
37 self.set_text_color(255, 255, 255)
38 self.cell(0, 10, title, 0, 1, 'L', 1)
39 self.set_text_color(0, 0, 0)
40 self.ln(4)
41
42 def section_title(self, title: str):
43 """Add section title"""
44 self.set_font('Arial', 'B', 12)
45 self.set_text_color(52, 73, 94)
46 self.cell(0, 8, title, 0, 1, 'L')
47 self.set_text_color(0, 0, 0)
48 self.ln(2)
49
50 def body_text(self, text: str):
51 """Add body text"""
52 self.set_font('Arial', '', 10)
53 self.multi_cell(0, 6, text)
54 self.ln(2)
55
56 def add_score_bar(self, label: str, score: float, max_score: float = 100):
57 """Add visual score bar"""
58 self.set_font('Arial', '', 10)
59 self.cell(60, 8, label, 0, 0)
60
61 # Draw score bar
62 bar_width = 100
63 fill_width = (score / max_score) * bar_width
64
65 x = self.get_x()
66 y = self.get_y()
67
68 # Background bar
69 self.set_fill_color(220, 220, 220)
70 self.rect(x, y + 2, bar_width, 6, 'F')
71
72 # Score bar with color based on score
73 if score >= 80:
74 self.set_fill_color(46, 204, 113) # Green
75 elif score >= 60:
76 self.set_fill_color(241, 196, 15) # Yellow
77 else:
78 self.set_fill_color(231, 76, 60) # Red
79
80 self.rect(x, y + 2, fill_width, 6, 'F')
81
82 # Score text
83 self.set_xy(x + bar_width + 5, y)
84 self.set_font('Arial', 'B', 10)
85 self.cell(20, 8, f'{score:.1f}', 0, 1)
86
87
88class ReportGenerator:
89 """Generate comprehensive interview performance reports"""
90
91 def __init__(self):
92 """Initialize report generator"""
93 self.report_data = {}
94
95 def _clean_text(self, text: str) -> str:
96 """Clean text for PDF rendering - remove problematic characters"""
97 if not text:
98 return ""
99 # Remove or replace problematic characters
100 text = text.replace('–', '-').replace('—', '-')
101 text = text.replace(''', "'").replace(''', "'")
102 text = text.replace('"', '"').replace('"', '"')
103 text = text.replace('…', '...')
104 # Remove any non-ASCII characters that might cause issues
105 text = ''.join(char if ord(char) < 128 else ' ' for char in text)
106 return text.strip()
107
108 def generate_report(self, interview_data: Dict, output_path: str) -> bool:
109 """
110 Generate PDF report from interview data
111
112 Args:
113 interview_data: Dictionary containing all interview data
114 output_path: Path to save PDF report
115
116 Returns:
117 True if successful, False otherwise
118 """
119 try:
120 pdf = InterviewReport()
121 pdf.add_page()
122
123 # Title and metadata
124 self._add_report_header(pdf, interview_data)
125
126 # Overall performance summary
127 self._add_overall_summary(pdf, interview_data)
128
129 # Individual question analysis
130 self._add_question_analysis(pdf, interview_data)
131
132 # Detailed metrics
133 self._add_detailed_metrics(pdf, interview_data)
134
135 # Video analysis
136 if 'video_analysis' in interview_data:
137 self._add_video_analysis(pdf, interview_data['video_analysis'])
138
139 # Recommendations
140 self._add_recommendations(pdf, interview_data)
141
142 # Save PDF
143 pdf.output(output_path)
144 print(f"✅ Report generated successfully: {output_path}")
145 return True
146
147 except Exception as e:
148 print(f"❌ Error generating report: {e}")
149 return False
150
151 def _add_report_header(self, pdf: InterviewReport, data: Dict):
152 """Add report header with candidate info"""
153 pdf.set_font('Arial', '', 11)
154
155 # Date and time
156 timestamp = data.get('timestamp', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
157 pdf.cell(0, 8, f'Date: {timestamp}', 0, 1)
158
159 # Candidate info if available
160 if 'candidate_name' in data:
161 pdf.cell(0, 8, f'Candidate: {data["candidate_name"]}', 0, 1)
162
163 if 'position' in data:
164 pdf.cell(0, 8, f'Position: {data["position"]}', 0, 1)
165
166 pdf.ln(5)
167
168 def _add_overall_summary(self, pdf: InterviewReport, data: Dict):
169 """Add overall performance summary"""
170 pdf.chapter_title('Overall Performance Summary')
171
172 overall_score = data.get('overall_score', 0)
173
174 # Performance rating
175 if overall_score >= 85:
176 rating = "Excellent"
177 color = (46, 204, 113)
178 elif overall_score >= 70:
179 rating = "Good"
180 color = (52, 152, 219)
181 elif overall_score >= 50:
182 rating = "Average"
183 color = (241, 196, 15)
184 else:
185 rating = "Needs Improvement"
186 color = (231, 76, 60)
187
188 pdf.set_font('Arial', 'B', 14)
189 pdf.set_text_color(*color)
190 pdf.cell(0, 10, f'Overall Rating: {rating} ({overall_score:.1f}/100)', 0, 1)
191 pdf.set_text_color(0, 0, 0)
192 pdf.ln(5)
193
194 # Key scores
195 pdf.section_title('Key Performance Metrics')
196
197 metrics = data.get('metrics', {})
198 pdf.add_score_bar('Content Quality', metrics.get('content_score', 0))
199 pdf.add_score_bar('Communication Clarity', metrics.get('clarity_score', 0))
200 pdf.add_score_bar('Confidence Level', metrics.get('confidence_score', 0))
201 pdf.add_score_bar('Professionalism', metrics.get('professionalism_score', 0))
202
203 pdf.ln(5)
204
205 def _add_question_analysis(self, pdf: InterviewReport, data: Dict):
206 """Add individual question analysis"""
207 pdf.add_page()
208 pdf.chapter_title('Question-by-Question Analysis')
209
210 questions = data.get('questions', [])
211
212 for i, q_data in enumerate(questions, 1):
213 pdf.section_title(f'Question {i}')
214
215 # Question text
216 pdf.set_font('Arial', 'I', 10)
217 question_text = self._clean_text(q_data.get("question", "N/A"))
218 pdf.multi_cell(0, 6, f'Q: {question_text}')
219 pdf.ln(2)
220
221 # Answer summary
222 pdf.set_font('Arial', '', 10)
223 answer = self._clean_text(q_data.get('answer', 'No answer provided'))
224 if len(answer) > 200:
225 answer = answer[:200] + '...'
226 pdf.multi_cell(0, 6, f'A: {answer}')
227 pdf.ln(3)
228
229 # Scores
230 analysis = q_data.get('analysis', {})
231 score = analysis.get('overall_score', 0)
232
233 pdf.set_font('Arial', 'B', 10)
234 pdf.cell(40, 6, 'Score:', 0, 0)
235
236 # Color-coded score
237 if score >= 80:
238 pdf.set_text_color(46, 204, 113)
239 elif score >= 60:
240 pdf.set_text_color(241, 196, 15)
241 else:
242 pdf.set_text_color(231, 76, 60)
243
244 pdf.cell(30, 6, f'{score:.1f}/100', 0, 1)
245 pdf.set_text_color(0, 0, 0)
246
247 # Key feedback points
248 feedback = q_data.get('feedback', [])
249 if feedback:
250 pdf.set_font('Arial', '', 9)
251 pdf.cell(0, 6, 'Feedback:', 0, 1)
252 for fb in feedback[:3]: # Top 3 feedback points
253 cleaned_fb = self._clean_text(fb)
254 if cleaned_fb: # Only add if there's content after cleaning
255 pdf.set_x(pdf.l_margin + 5) # Indent
256 # Use smaller width to prevent overflow
257 pdf.multi_cell(pdf.w - pdf.l_margin - pdf.r_margin - 10, 5, f'- {cleaned_fb}')
258
259 pdf.ln(5)
260
261 # Add page break if needed
262 if i < len(questions) and pdf.get_y() > 240:
263 pdf.add_page()
264
265 def _add_detailed_metrics(self, pdf: InterviewReport, data: Dict):
266 """Add detailed performance metrics"""
267 pdf.add_page()
268 pdf.chapter_title('Detailed Performance Metrics')
269
270 metrics = data.get('detailed_metrics', {})
271
272 # Content Analysis
273 pdf.section_title('Content Analysis')
274 content = metrics.get('content', {})
275 pdf.body_text(self._clean_text(f"Average Word Count: {content.get('avg_word_count', 0):.0f} words"))
276 pdf.body_text(self._clean_text(f"Examples Provided: {'Yes' if content.get('has_examples', False) else 'No'}"))
277 pdf.body_text(self._clean_text(f"Quantifiable Achievements: {'Yes' if content.get('has_quantification', False) else 'No'}"))
278 pdf.ln(3)
279
280 # Communication Analysis
281 pdf.section_title('Communication Analysis')
282 comm = metrics.get('communication', {})
283 pdf.body_text(self._clean_text(f"Speaking Clarity: {comm.get('clarity', 0):.1f}/100"))
284 pdf.body_text(self._clean_text(f"Filler Words (avg per answer): {comm.get('filler_words', 0):.1f}"))
285 pdf.body_text(self._clean_text(f"Professional Language: {comm.get('professionalism', 'N/A')}"))
286 pdf.ln(3)
287
288 # Audio Analysis
289 pdf.section_title('Voice Analysis')
290 audio = metrics.get('audio', {})
291 pdf.body_text(self._clean_text(f"Average Response Duration: {audio.get('avg_duration', 0):.1f} seconds"))
292 pdf.body_text(self._clean_text(f"Speaking Rate: {audio.get('speaking_rate', 0):.0f} words per minute"))
293 pdf.body_text(self._clean_text(f"Voice Confidence: {audio.get('confidence', 0):.1f}/100"))
294 pdf.ln(3)
295
296 def _add_video_analysis(self, pdf: InterviewReport, video_data: Dict):
297 """Add video analysis section"""
298 pdf.section_title('Visual Presence Analysis')
299
300 pdf.body_text(self._clean_text(f"Eye Contact: {video_data.get('eye_contact_percentage', 0):.1f}%"))
301 pdf.body_text(self._clean_text(f"Dominant Emotion: {video_data.get('dominant_emotion', 'N/A').title()}"))
302 pdf.body_text(self._clean_text(f"Posture: {video_data.get('dominant_posture', 'N/A').replace('_', ' ').title()}"))
303 pdf.body_text(self._clean_text(f"Engagement Score: {video_data.get('engagement_score', 0):.1f}/100"))
304
305 pdf.ln(5)
306
307 def _add_recommendations(self, pdf: InterviewReport, data: Dict):
308 """Add personalized recommendations"""
309 pdf.add_page()
310 pdf.chapter_title('Personalized Recommendations')
311
312 overall_score = data.get('overall_score', 0)
313
314 # Strengths
315 pdf.section_title('Key Strengths')
316 strengths = data.get('strengths', [
317 'Good communication skills',
318 'Relevant experience highlighted',
319 'Professional demeanor'
320 ])
321
322 for strength in strengths[:5]:
323 pdf.set_font('Arial', '', 10)
324 pdf.set_x(pdf.l_margin + 5)
325 cleaned_strength = self._clean_text(strength)
326 if cleaned_strength:
327 pdf.multi_cell(pdf.w - pdf.l_margin - pdf.r_margin - 10, 6, f'* {cleaned_strength}')
328
329 pdf.ln(5)
330
331 # Areas for improvement
332 pdf.section_title('Areas for Improvement')
333 improvements = data.get('improvements', [
334 'Provide more specific examples',
335 'Reduce use of filler words',
336 'Improve eye contact'
337 ])
338
339 for improvement in improvements[:5]:
340 pdf.set_font('Arial', '', 10)
341 pdf.set_x(pdf.l_margin + 5)
342 cleaned_improvement = self._clean_text(improvement)
343 if cleaned_improvement:
344 pdf.multi_cell(pdf.w - pdf.l_margin - pdf.r_margin - 10, 6, f'- {cleaned_improvement}')
345
346 pdf.ln(5)
347
348 # Action items
349 pdf.section_title('Action Items for Next Interview')
350 action_items = self._generate_action_items(data)
351
352 for i, item in enumerate(action_items, 1):
353 pdf.set_font('Arial', '', 10)
354 cleaned_item = self._clean_text(item)
355 if cleaned_item:
356 pdf.multi_cell(0, 6, f'{i}. {cleaned_item}')
357 pdf.ln(2)
358
359 # Final note
360 pdf.ln(10)
361 pdf.set_font('Arial', 'I', 10)
362 pdf.set_text_color(52, 73, 94)
363 final_note = self._clean_text(
364 'Remember: Practice makes perfect! Use this feedback to prepare for your next interview. '
365 'Good luck!'
366 )
367 pdf.multi_cell(0, 6, final_note)
368
369 def _generate_action_items(self, data: Dict) -> List[str]:
370 """Generate specific action items based on performance"""
371 action_items = []
372
373 metrics = data.get('metrics', {})
374
375 if metrics.get('content_score', 100) < 70:
376 action_items.append(
377 'Practice using the STAR method (Situation, Task, Action, Result) to structure your answers'
378 )
379
380 if metrics.get('clarity_score', 100) < 70:
381 action_items.append(
382 'Record yourself answering common questions and listen for clarity improvements'
383 )
384
385 if metrics.get('confidence_score', 100) < 70:
386 action_items.append(
387 'Build confidence by researching the company thoroughly and preparing answers in advance'
388 )
389
390 video_data = data.get('video_analysis', {})
391 if video_data.get('eye_contact_percentage', 100) < 60:
392 action_items.append(
393 'Practice maintaining eye contact with the camera during mock interviews'
394 )
395
396 if not action_items:
397 action_items = [
398 'Continue practicing with diverse question types',
399 'Research industry-specific terminology and trends',
400 'Refine your personal stories and achievements'
401 ]
402
403 return action_items[:5]
404
405
406if __name__ == "__main__":
407 # Test the report generator
408 print("Report Generator Module - Test Mode")
409 print("=" * 50)
410
411 # Sample interview data
412 sample_data = {
413 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
414 'candidate_name': 'John Doe',
415 'position': 'Senior Software Engineer',
416 'overall_score': 78.5,
417 'metrics': {
418 'content_score': 82,
419 'clarity_score': 75,
420 'confidence_score': 80,
421 'professionalism_score': 77
422 },
423 'questions': [
424 {
425 'question': 'Tell me about yourself.',
426 'answer': 'I am a software engineer with 5 years of experience...',
427 'analysis': {'overall_score': 85},
428 'feedback': ['Great structure', 'Good examples']
429 },
430 {
431 'question': 'Describe a challenging project.',
432 'answer': 'In my previous role, I worked on...',
433 'analysis': {'overall_score': 72},
434 'feedback': ['Add more quantifiable results', 'Good detail']
435 }
436 ],
437 'detailed_metrics': {
438 'content': {'avg_word_count': 95, 'has_examples': True, 'has_quantification': False},
439 'communication': {'clarity': 75, 'filler_words': 2.5, 'professionalism': 'Good'},
440 'audio': {'avg_duration': 45, 'speaking_rate': 120, 'confidence': 80}
441 },
442 'video_analysis': {
443 'eye_contact_percentage': 65,
444 'dominant_emotion': 'confident',
445 'dominant_posture': 'centered',
446 'engagement_score': 75
447 },
448 'strengths': [
449 'Clear communication',
450 'Relevant examples',
451 'Professional demeanor'
452 ],
453 'improvements': [
454 'Add more quantifiable achievements',
455 'Maintain better eye contact',
456 'Reduce filler words'
457 ]
458 }
459
460 generator = ReportGenerator()
461 output_file = '/home/claude/sample_interview_report.pdf'
462
463 if generator.generate_report(sample_data, output_file):
464 print(f"\n✅ Sample report created: {output_file}")
465 else:
466 print("\n❌ Failed to create sample report")