from typing import Dict from benchmarks import get_benchmark, get_missing_element_recommendation, GOAL_MAPPING class ReportGenerator: def generate( self, analysis: Dict, industry: str, campaign_goal: str ) -> Dict: """ Generate actionable report with recommendations. Args: analysis: Output from NarrativeClassifier industry: Selected industry campaign_goal: Selected campaign goal Returns: Complete report with summary, segments, and recommendations """ benchmark = get_benchmark(industry, campaign_goal) goal_key = GOAL_MAPPING.get(campaign_goal, "retention") # Build recommendations recommendations = [] # 1. Check if current arc matches optimal current_arc = analysis.get('story_arc', 'Unknown') optimal_arc = benchmark.get('best_arc', 'Unknown') arc_matches = self._arcs_match(current_arc, optimal_arc) if not arc_matches and optimal_arc != 'Unknown': recommendations.append({ "priority": "HIGH", "type": "arc_mismatch", "action": f"Consider restructuring to {optimal_arc} arc", "expected_impact": f"+{benchmark.get('uplift_percent', '?')}% {goal_key}", "reasoning": benchmark.get('recommendation', '') }) # 2. Check missing elements missing = analysis.get('missing_elements', []) for element in missing: rec = get_missing_element_recommendation(element) recommendations.append({ "priority": "MEDIUM" if element in ['Hook', 'Call-to-Action'] else "LOW", "type": "missing_element", "action": f"Add {element}", "expected_impact": rec.get('impact', ''), "reasoning": rec.get('suggestion', '') }) # 3. Story presence recommendation if not analysis.get('has_story', False): recommendations.append({ "priority": "MEDIUM", "type": "no_story", "action": "Consider adding narrative elements", "expected_impact": "+5-10% retention", "reasoning": "Ads with stories show 5-10% better retention than feature-focused ads" }) # Sort by priority priority_order = {"HIGH": 0, "MEDIUM": 1, "LOW": 2} recommendations.sort(key=lambda x: priority_order.get(x['priority'], 3)) return { "summary": { "has_story": analysis.get('has_story', False), "story_explanation": analysis.get('story_explanation', ''), "detected_arc": current_arc, "optimal_arc_for_goal": optimal_arc, "arc_matches_optimal": arc_matches, "potential_uplift": f"+{benchmark.get('uplift_percent', '?')}%" }, "segments": analysis.get('segments', []), "detected_sequence": analysis.get('detected_sequence', []), "missing_elements": missing, "recommendations": recommendations, "benchmark": benchmark } def _arcs_match(self, current: str, optimal: str) -> bool: """Check if arcs match (fuzzy matching).""" if current == optimal: return True # Normalize current_norm = current.lower().replace('-', '').replace(' ', '') optimal_norm = optimal.lower().replace('-', '').replace(' ', '') return current_norm == optimal_norm