StoryLens / report_generator.py
Marek4321's picture
Upload 13 files
6bdfadc verified
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