""" Unified Rendering Layer - Rooting Future Strategy Engine v5.4 Consolidates structured_renderer, executive_report, and methodology_section. """ import os import re import json import logging from datetime import datetime from pathlib import Path from typing import Dict, List, Optional, Any # Core Data Models from data_models import ( DataPoint, StructuredSection, StructuredPlan, DataType, ConfidenceLevel, DeviationType, Source, Benchmark, SourceType, BenchmarkDatabase ) # Utils & Analyzers from data_estimator import estimate_missing_financials, DataTier from chart_generator import ( generate_revenue_pie_chart, generate_benchmark_comparison, generate_gap_analysis_chart, embed_chart_in_html, generate_financial_charts_for_report ) from stw_analyzer import calculate_stw_progress, STWAnalyzer from stw_matrix import get_category_color, get_category_icon, STWCategory from club_identity import get_club_identity logger = logging.getLogger(__name__) # ============================================================================= # CONSTANTS & FALLBACKS # ============================================================================= MICRO_OBJECTIVES_FALLBACKS = { 'CREAZIONE E SVILUPPO IDENTITÀ TECNICA': [ 'Definire modulo tattico unificato (es. 4-3-3) per tutte le categorie', 'Formare lo staff tecnico su metodologia di gioco comune', 'Implementare sistema di valutazione performance standardizzato', 'Creare protocollo allenamenti settimanale condiviso tra categorie' ], 'COSTRUZIONE E RINNOVAMENTO STRUTTURE': [ 'Mappare stato attuale impianti con report fotografico dettagliato', 'Prioritizzare interventi su campo allenamento settore giovanile', 'Ottenere certificazioni sicurezza aggiornate per tutti gli impianti', 'Installare sistema illuminazione LED su campo principale' ], 'SVILUPPO AREA COMUNICAZIONE': [ 'Pubblicare 3 post/settimana sui social media con calendario editoriale', 'Creare newsletter mensile per tesserati e sponsor', 'Implementare area stampa digitale sul sito web', 'Avviare collaborazione con media locali per copertura partite' ], 'SVILUPPO AREA MARKETING': [ 'Lanciare campagna sponsor per stagione 2026/27 con target €50K', 'Creare merchandising ufficiale (maglie, sciarpe) entro marzo', 'Implementare programma fedeltà per abbonati', 'Organizzare 2 eventi corporate per attrarre nuovi partner' ], 'SVILUPPO BRAND IDENTITY': [ 'Ridisegnare logo e brand guidelines entro aprile 2026', 'Unificare comunicazione visiva su tutti i canali', 'Creare brand book digitale per uso interno/esterno', 'Registrare marchio presso UIBM per protezione legale' ], 'SVILUPPO AREA COMMERCIALE': [ 'Mappare potenziali sponsor locali (target list di 30 aziende)', 'Creare presentation deck commerciale con pacchetti sponsor', 'Assumere commerciale part-time per gestione sponsor', 'Attivare vendita biglietti online su piattaforma dedicata' ], 'SVILUPPO INCLUSIONE E UGUAGLIANZA': [ 'Creare squadra femminile o accordo con club femminile locale', 'Implementare protocollo anti-discriminazione in tutti gli eventi', 'Organizzare torneo giovanile inclusivo aperto a tutti', 'Formare staff su gestione diversità e inclusione' ], 'PROTEZIONE BAMBINI/E E GIOVANI': [ 'Certificare tutti gli allenatori giovanili con corso Safeguarding', 'Implementare protocollo tutela minori conforme FIGC', 'Nominare responsabile protezione minori nel club', 'Attivare assicurazione specifica per settore giovanile' ], 'RISORSE UMANE': [ 'Digitalizzare gestione presenze staff con sistema HR cloud', 'Creare organigramma chiaro con ruoli e responsabilità definiti', 'Implementare valutazione annuale performance per staff tecnico', 'Attivare convezioni welfare per dipendenti (palestra, assicurazioni)' ] } SYSTEM_SOURCES = { 'questionnaire': { 'name': 'Questionari Club', 'type': 'tier1_fact', 'description': 'Dati forniti direttamente dal club tramite questionari compilati', 'confidence': 1.0 }, 'transfermarkt': { 'name': 'Transfermarkt', 'type': 'tier1_fact', 'description': 'Valori di mercato rosa, statistiche giocatori', 'confidence': 0.90, 'url': 'https://www.transfermarkt.it' }, 'figc_report': { 'name': 'Report Calcio FIGC 2024', 'type': 'tier1_fact', 'description': 'Benchmark finanziari e sportivi ufficiali per categoria', 'confidence': 0.95, 'url': 'https://www.figc.it/it/federazione/report-calcio/' } } # ============================================================================= # HELPER FUNCTIONS (INTERNAL) # ============================================================================= def _clean_text(text: str) -> str: if not text: return "" text = re.sub(r'\*\*|\*|#{1,4}\s*', '', text) text = re.sub(r'\s+', ' ', text).strip() if text.startswith(':'): text = text[1:].strip() return text def _darken_color(hex_color: str, factor: float = 0.2) -> str: hex_color = hex_color.lstrip('#') if len(hex_color) != 6: return "#000000" r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) r = int(r * (1 - factor)) g = int(g * (1 - factor)) b = int(b * (1 - factor)) return f'#{r:02x}{g:02x}{b:02x}' def _get_contrast_color(hex_color: str) -> str: hex_color = hex_color.lstrip('#') if len(hex_color) != 6: return "#ffffff" r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 return '#ffffff' if luminance < 0.5 else '#1a1a1a' def _extract_key_points(content: str, max_points: int = 5) -> List[str]: if not content: return [] points = [] bold_with_content = re.findall(r'\*\*([A-Za-zÀ-ÿ][^*]{5,50})(?::\*\*|\*\*:)\s*([^*\n]{10,200})', content) for title, desc in bold_with_content: title = _clean_text(title) desc = _clean_text(desc) first_sentence = re.split(r'(?<=[.!?])\s', desc)[0] if desc else '' if first_sentence and len(first_sentence) > 15: point = f"{title}: {first_sentence}" if point not in points: points.append(point) if len(points) >= max_points: return points return points[:max_points] # ============================================================================= # PLAN RENDERER # ============================================================================= class PlanRenderer: """ Unified rendering engine for strategic plans. """ def __init__(self, output_dir: str = "output"): self.output_dir = Path(output_dir) self.output_dir.mkdir(exist_ok=True, parents=True) self.footnotes = [] self.footnote_counter = 0 # --- STRUCTURED RENDERING --- def render_structured(self, plan: StructuredPlan) -> str: html = self.render_structured_html(plan) filename = f"{plan.club_name.replace(' ', '_')}_Structured_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html" filepath = self.output_dir / filename with open(filepath, 'w', encoding='utf-8') as f: f.write(html) return str(filepath) def render_structured_html(self, plan: StructuredPlan) -> str: self.footnotes = [] self.footnote_counter = 0 return f'''
Piano Strategico Triennale