File size: 20,640 Bytes
db70c95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# modules/report_generator.py
import datetime
import os
import numpy as np
import matplotlib.pyplot as plt
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak, Image, HRFlowable
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER
from reportlab.lib.units import inch
from reportlab.lib import colors
from modules.llm_handler import generate_coaching_feedback
import config
import tempfile

def define_skill_areas(coaching_type):
    """Define key skill areas based on product management coaching type."""
    skill_mapping = {
        'Product Strategy & Vision': ['Strategic Thinking', 'Vision Articulation', 'Business Alignment'],
        'Market Research & Analysis': ['Research Skills', 'Data Analysis', 'Market Understanding'],
        'User Experience & Design Thinking': ['User Empathy', 'Design Process', 'Problem Solving'],
        'Product Roadmap Planning': ['Prioritization', 'Planning', 'Communication'],
        'Metrics & Analytics': ['Data Literacy', 'Analytical Thinking', 'Decision Making'],
        'Stakeholder Management': ['Communication', 'Negotiation', 'Relationship Building'],
        'Product Launch Strategy': ['Execution', 'Planning', 'Cross-functional Leadership'],
        'Competitive Analysis': ['Market Analysis', 'Strategic Thinking', 'Opportunity Recognition'],
        'Feature Prioritization': ['Decision Making', 'Framework Application', 'Trade-off Analysis'],
        'Customer Development': ['Customer Empathy', 'Research Skills', 'Insight Generation'],
        'Resume & Application Strategy': ['Application Skills', 'Personal Branding', 'Interview Preparation']
    }
    return skill_mapping.get(coaching_type, ['Strategic Thinking', 'Problem Solving', 'Communication'])

def create_coaching_progress_chart(labels, file_path):
    """Create a visual representation of coaching areas covered with error handling."""
    try:
        plt.clf()  # Clear any existing plots
        fig, ax = plt.subplots(figsize=(8, 6))
        y_pos = np.arange(len(labels))
        
        # Create a progress-style chart
        progress_values = [100] * len(labels)  # All areas were covered
        colors_list = plt.cm.Blues(np.linspace(0.4, 0.8, len(labels)))
        
        bars = ax.barh(y_pos, progress_values, color=colors_list, alpha=0.7)
        
        ax.set_yticks(y_pos)
        ax.set_yticklabels(labels)
        ax.set_xlabel('Coaching Coverage (%)')
        ax.set_title('Product Management Skills Coaching Session', fontsize=14, fontweight='bold')
        ax.set_xlim(0, 100)
        
        # Add checkmarks to indicate completion
        for i, bar in enumerate(bars):
            ax.text(bar.get_width() - 10, bar.get_y() + bar.get_height()/2, 
                    '✓', ha='center', va='center', fontsize=16, color='white', fontweight='bold')
        
        plt.tight_layout()
        plt.savefig(file_path, dpi=300, bbox_inches='tight')
        plt.close(fig)  # Explicitly close the figure
        print(f"✅ Chart saved to: {file_path}")
        return True
        
    except Exception as e:
        print(f"❌ Error creating chart: {e}")
        return False
    
    plt.tight_layout()
    plt.savefig(file_path, dpi=300, bbox_inches='tight')
    plt.close(fig)
    print(f"📈 Coaching progress chart saved to {file_path}")

def clean_text_for_pdf(text):
    """Remove markdown formatting and clean text for professional PDF appearance."""
    import re
    
    # Remove markdown bold/italic formatting - handle nested patterns
    text = re.sub(r'\*\*\*(.*?)\*\*\*', r'\1', text)  # Remove ***bold italic***
    text = re.sub(r'\*\*(.*?)\*\*', r'\1', text)      # Remove **bold**
    text = re.sub(r'\*(.*?)\*', r'\1', text)          # Remove *italic*
    text = re.sub(r'__(.*?)__', r'\1', text)          # Remove __bold__
    text = re.sub(r'_(.*?)_', r'\1', text)            # Remove _italic_
    
    # Clean up section headers (remove markdown)
    text = re.sub(r'#{1,6}\s*', '', text)             # Remove # headers
    text = re.sub(r'\*\*([A-Z\s]+[A-Za-z]):\*\*', r'\1:', text)  # Clean section headers with colons
    text = re.sub(r'\*\*([A-Z][A-Za-z\s]*)\*\*', r'\1', text)    # Clean other bold headers
    
    # Remove markdown links
    text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', text)
    
    # Clean up bullet points and list markers
    text = re.sub(r'^\s*[-\*\+•]\s+', '• ', text, flags=re.MULTILINE)
    text = re.sub(r'^\s*\d+\.\s+', '• ', text, flags=re.MULTILINE)  # Convert numbered lists
    
    # Remove extra asterisks that might be left over
    text = re.sub(r'\*+', '', text)
    text = re.sub(r'_+', '', text)
    
    # Clean up multiple spaces and normalize line breaks
    text = re.sub(r'\n\s*\n\s*\n+', '\n\n', text)
    text = re.sub(r'\s+', ' ', text)
    text = text.strip()
    
    return text

def format_feedback_sections(feedback_text):
    """Break feedback into structured sections for better visual presentation."""
    # First, clean the text of markdown formatting
    cleaned_text = clean_text_for_pdf(feedback_text)
    
    sections = []
    current_section = ""
    current_title = ""
    
    lines = cleaned_text.split('\n')
    
    for line in lines:
        line = line.strip()
        if not line:
            continue
            
        # Check if line is a section header (contains keywords)
        if any(keyword in line.upper() for keyword in ['STRENGTHS:', 'AREAS FOR GROWTH:', 'FRAMEWORK', 'METRICS', 'NEXT STEPS:', 'INSIGHT:', 'SCORES:', 'KEY STRENGTHS', 'RECOMMENDATIONS']):
            # Save previous section
            if current_title and current_section:
                sections.append({
                    'title': current_title,
                    'content': current_section.strip()
                })
            
            # Start new section
            current_title = line.replace(':', '').strip()
            current_section = ""
        else:
            # Add to current section
            current_section += line + " "
    
    # Add final section
    if current_title and current_section:
        sections.append({
            'title': current_title,
            'content': current_section.strip()
        })
    
    return sections

def generate_pdf_report(coaching_data, file_path):
    """Generate a professional product management coaching report without markdown formatting."""
    try:
        doc = SimpleDocTemplate(file_path, pagesize=(8.5 * inch, 11 * inch))
        styles = getSampleStyleSheet()
        
        # Professional styles for coaching report - only add if they don't exist
        def safe_add_style(styles, name, **kwargs):
            """Safely add a style only if it doesn't already exist"""
            if name not in styles:
                styles.add(ParagraphStyle(name=name, **kwargs))
        
        safe_add_style(styles, 'ReportTitle',
            parent=styles['Heading1'], 
            fontSize=24, 
            alignment=TA_CENTER, 
            spaceAfter=20, 
            textColor=colors.darkblue,
            fontName='Helvetica-Bold'
        )
        
        safe_add_style(styles, 'SectionHeader',
            parent=styles['Heading2'], 
            fontSize=16, 
            spaceBefore=15, 
            spaceAfter=10, 
            textColor=colors.darkblue,
            fontName='Helvetica-Bold'
        )
        
        safe_add_style(styles, 'SubHeader',
            parent=styles['Heading3'], 
            fontSize=14, 
            spaceBefore=12, 
            spaceAfter=8, 
            textColor=colors.darkgreen,
            fontName='Helvetica-Bold'
        )
        
        safe_add_style(styles, 'BodyText',
            parent=styles['Normal'],
            fontSize=11, 
            spaceAfter=12,
            spaceBefore=6, 
            alignment=TA_JUSTIFY,
            fontName='Helvetica'
        )
        
        safe_add_style(styles, 'BulletText',
            parent=styles['Normal'],
            fontSize=11, 
            spaceAfter=8,
            spaceBefore=4,
            leftIndent=20,
            fontName='Helvetica'
        )
        
        safe_add_style(styles, 'BulletPoint',
            parent=styles['Normal'],
            fontSize=11, 
            spaceAfter=6,
            spaceBefore=3,
            leftIndent=20,
            fontName='Helvetica'
        )
        
        safe_add_style(styles, 'ScoreText',
            parent=styles['Normal'],
            fontSize=12, 
            spaceAfter=8,
            spaceBefore=4, 
            textColor=colors.blue,
            fontName='Helvetica-Bold'
        )
        
        safe_add_style(styles, 'HighlightBox',
            parent=styles['Normal'],
            fontSize=11,
            spaceAfter=12,
            spaceBefore=12,
            leftIndent=15,
            rightIndent=15,
            borderWidth=1,
            borderColor=colors.lightgrey,
            backColor=colors.lightgrey,
            fontName='Helvetica'
        )
        
        print("✅ Stylesheet created successfully")
        
        story = []
        
        # Title Page
        story.append(Paragraph("Personal AI Product Coach", styles['ReportTitle']))
        story.append(Paragraph("Product Management Coaching Report", styles['SectionHeader']))
        story.append(Spacer(1, 0.5 * inch))
        
        # Executive Summary Table
        story.append(Paragraph("Executive Summary", styles['SectionHeader']))
        story.append(Paragraph(f"Participant: {coaching_data.get('name', 'Product Manager')}", styles['BodyText']))
        story.append(Paragraph(f"Coaching Focus: {coaching_data.get('type', 'General PM Coaching')}", styles['BodyText']))
        story.append(Paragraph(f"Session Date: {datetime.datetime.now().strftime('%B %d, %Y')}", styles['BodyText']))
        story.append(Paragraph(f"Scenarios Completed: {len(coaching_data.get('q_and_a', []))}", styles['BodyText']))
        
        # Calculate overall session performance
        session_scores = []
        for scenario in coaching_data.get('q_and_a', []):
            if 'overall_score' in scenario and scenario['overall_score'] > 0:
                session_scores.append(scenario['overall_score'])
        
        if session_scores:
            avg_score = sum(session_scores) / len(session_scores)
            story.append(Paragraph(f"Session Average Score: {avg_score:.1f}/10", styles['ScoreText']))
        
        story.append(PageBreak())

        # Overall Performance Analysis
        story.append(Paragraph("Overall Performance Analysis", styles['SectionHeader']))
        
        # Generate comprehensive feedback with error handling
        try:
            overall_feedback = generate_coaching_feedback(coaching_data.get('q_and_a', []), 
                                                         coaching_data.get('type', 'General'), 
                                                         coaching_data.get('name', 'Product Manager'))
            
            # Format the feedback into structured sections
            feedback_sections = format_feedback_sections(overall_feedback)
            
            if feedback_sections:
                for section in feedback_sections:
                    # Add section title with proper styling
                    story.append(Paragraph(section['title'], styles['SubHeader']))
                    story.append(Spacer(1, 0.1 * inch))
                    
                    # Split content into bullet points if it contains bullet indicators
                    content = section['content']
                    if '•' in content:
                        # Handle bullet points
                        bullets = [line.strip() for line in content.split('•') if line.strip()]
                        for bullet in bullets:
                            if bullet:
                                story.append(Paragraph(f"• {bullet}", styles['BulletPoint']))
                    else:
                        # Handle regular paragraphs
                        paragraphs = [p.strip() for p in content.split('.') if p.strip()]
                        for para in paragraphs:
                            if para and len(para) > 10:  # Avoid very short fragments
                                story.append(Paragraph(f"{para}.", styles['BodyText']))
                    
                    story.append(Spacer(1, 0.2 * inch))
            else:
                # Fallback if no sections found - display as clean text
                clean_feedback = clean_text_for_pdf(overall_feedback)
                story.append(Paragraph(clean_feedback, styles['BodyText']))
                
        except Exception as feedback_error:
            print(f"⚠️ Error generating overall feedback: {feedback_error}")
            story.append(Paragraph("Great work in this coaching session! You demonstrated solid product management thinking and approach to the scenarios presented.", styles['BodyText']))
        
        story.append(Spacer(1, 0.3 * inch))

        # Add progress chart with error handling
        try:
            skill_labels = define_skill_areas(coaching_data.get('type', 'General'))
            chart_path = os.path.join(config.REPORT_FOLDER, "coaching_progress.png")
            if os.path.exists(chart_path): 
                os.remove(chart_path)
            
            if create_coaching_progress_chart(skill_labels, chart_path):
                story.append(Image(chart_path, width=6*inch, height=4*inch, hAlign='CENTER'))
            else:
                story.append(Paragraph("Skills covered in this coaching session:", styles['SubHeader']))
                for skill in skill_labels:
                    story.append(Paragraph(f"• {skill}", styles['BodyText']))
        except Exception as chart_error:
            print(f"⚠️ Error creating chart: {chart_error}")
            story.append(Paragraph("Skills Development Summary", styles['SubHeader']))
            story.append(Paragraph("This coaching session covered key product management competencies.", styles['BodyText']))
        
        story.append(PageBreak())

        # Detailed Scenario Analysis
        story.append(Paragraph("Detailed Scenario Analysis", styles['SectionHeader']))
        story.append(Spacer(1, 0.2 * inch))
        
        for i, scenario in enumerate(coaching_data.get('q_and_a', [])):
            try:
                # Scenario Header with visual separation
                story.append(Paragraph(f"Scenario {i+1}", styles['SubHeader']))
                story.append(Spacer(1, 0.1 * inch))
                
                # Challenge Description in highlighted box
                story.append(Paragraph("Challenge:", styles['SubHeader']))
                clean_question = clean_text_for_pdf(scenario.get('question', 'Product management scenario'))
                story.append(Paragraph(clean_question, styles['HighlightBox']))
                story.append(Spacer(1, 0.15 * inch))
                
                # Participant's Response
                story.append(Paragraph("Your Approach:", styles['SubHeader']))
                clean_response = clean_text_for_pdf(scenario.get('response', 'Response provided'))
                story.append(Paragraph(clean_response, styles['BodyText']))
                story.append(Spacer(1, 0.1 * inch))
                
                # Score Display with visual emphasis
                if scenario.get('overall_score', 0) > 0:
                    score_text = f"Overall Score: {scenario['overall_score']}/10"
                    story.append(Paragraph(score_text, styles['ScoreText']))
                    story.append(Spacer(1, 0.1 * inch))
                
                # Coaching Feedback - break into sections
                story.append(Paragraph("Coaching Analysis:", styles['SubHeader']))
                feedback_text = scenario.get('feedback', 'Great work on this scenario!')
                feedback_sections = format_feedback_sections(feedback_text)
                
                if feedback_sections:
                    for section in feedback_sections:
                        if section['title'] and section['content']:
                            # Section title
                            story.append(Paragraph(f"{section['title']}:", styles['SubHeader']))
                            # Section content - already cleaned by format_feedback_sections
                            story.append(Paragraph(section['content'], styles['BulletText']))
                            story.append(Spacer(1, 0.05 * inch))
                else:
                    # Fallback - use original feedback with cleaning
                    clean_feedback = clean_text_for_pdf(feedback_text)
                    story.append(Paragraph(clean_feedback, styles['BodyText']))
                
                # Add separator between scenarios
                if i < len(coaching_data.get('q_and_a', [])) - 1:
                    story.append(Spacer(1, 0.3 * inch))
                    # Add a subtle line separator
                    story.append(HRFlowable(width="100%", thickness=1, lineCap='round', color=colors.lightgrey))
                    story.append(Spacer(1, 0.2 * inch))
                    
            except Exception as scenario_error:
                print(f"⚠️ Error processing scenario {i+1}: {scenario_error}")
                story.append(Paragraph(f"Scenario {i+1}: Completed successfully", styles['BodyText']))
                story.append(Spacer(1, 0.2 * inch))
        
        # Development Plan Section
        story.append(PageBreak())
        story.append(Paragraph("Your Product Management Development Plan", styles['SectionHeader']))
        story.append(Spacer(1, 0.2 * inch))
        
        # Focus Areas
        story.append(Paragraph("Recommended Focus Areas", styles['SubHeader']))
        focus_areas = [
            f"Continue practicing {coaching_data.get('type', 'product management').lower()} scenarios with real-world applications",
            "Explore and master relevant PM frameworks and methodologies",
            "Seek feedback from peers and mentors on your product management approach",
            "Apply these concepts in your current role or personal projects"
        ]
        for area in focus_areas:
            story.append(Paragraph(f"• {area}", styles['BulletText']))
        story.append(Spacer(1, 0.2 * inch))
        
        # Learning Resources
        story.append(Paragraph("Suggested Learning Resources", styles['SubHeader']))
        resources = [
            '"Inspired" by Marty Cagan - Product management fundamentals',
            '"The Lean Startup" by Eric Ries - Validation and iteration principles',
            '"Hooked" by Nir Eyal - User engagement and product psychology',
            'Product management communities and industry blogs for current best practices'
        ]
        for resource in resources:
            story.append(Paragraph(f"• {resource}", styles['BulletText']))
        story.append(Spacer(1, 0.2 * inch))
        
        # Next Steps
        story.append(Paragraph("Next Steps for Growth", styles['SubHeader']))
        next_steps_text = """Product management is a continuous learning journey. Use this coaching session as a foundation to build upon. 
        Regular practice with realistic scenarios, combined with framework application and stakeholder feedback, 
        will accelerate your development as a product manager.
        
        Remember to stay curious about user needs, data-driven in your decisions, and collaborative in your approach. 
        The best product managers never stop learning and adapting."""
        
        story.append(Paragraph(next_steps_text, styles['BodyText']))
        
        # Footer
        story.append(Spacer(1, 0.5 * inch))
        story.append(Paragraph("Personal AI Product Coach - Developing Product Management Excellence", 
                              styles['BodyText']))
        
        # Build the PDF
        doc.build(story)
        print(f"✅ Professional coaching report generated: {file_path}")
        return True
        
    except Exception as e:
        print(f"❌ Error generating PDF report: {e}")
        print(f"❌ Error details: {str(e)}")
        return False

# Legacy function for backward compatibility
def generate_holistic_feedback(interview_log):
    """Legacy function - redirects to generate_coaching_feedback."""
    return "This coaching session focused on developing key product management skills through practical scenarios."