CoolFace
Modelpublic

Prathmesh0001/interview-system

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