""" Utility per convertire StructuredPlan in markdown formattato per webapp """ import logging from typing import Dict from data_models import StructuredPlan, StructuredSection, DataPoint logger = logging.getLogger(__name__) def structured_plan_to_markdown(structured_plan: StructuredPlan) -> Dict[str, str]: """ Converte un StructuredPlan object in dictionary di sezioni markdown. Returns: Dict con chiavi: technical_sporting, youth_development, infrastructure, etc. """ sections_markdown = {} for section_key, section in structured_plan.sections.items(): try: markdown = _section_to_markdown(section) sections_markdown[section_key] = markdown logger.info(f"Converted section {section_key} to markdown ({len(markdown)} chars)") except Exception as e: logger.error(f"Error converting section {section_key}: {e}", exc_info=True) sections_markdown[section_key] = f"

Errore nella conversione: {str(e)}

" # Aggiungi executive summary se presente if structured_plan.executive_summary: sections_markdown['executive_summary'] = structured_plan.executive_summary logger.info(f"Converted {len(sections_markdown)} sections to markdown") return sections_markdown def _section_to_markdown(section: StructuredSection) -> str: """Converte una StructuredSection in markdown formattato""" if not section.data_points: return "

Sezione non disponibile

" md = f"# {section.title}\n\n" # Aggiungi data points organizzati md += "## Indicatori Chiave\n\n" for dp in section.data_points: # Header del data point con badge md += f"### {dp.label}\n\n" # Valore principale con units if dp.value is not None: md += f"**Valore:** {_format_value(dp.value, dp.unit)}\n\n" # Benchmark se disponibile if dp.benchmark and dp.benchmark.value is not None: md += f"**Benchmark {dp.benchmark.category}:** {_format_value(dp.benchmark.value, dp.unit)}\n\n" # Deviazione con colore if dp.deviation is not None: deviation_class = "success" if dp.deviation >= 0 else "warning" md += f"**Scostamento:** {dp.deviation:+.1f}%\n\n" # Fonte if dp.source: # Gestisci source.type che potrebbe essere un enum o una stringa if hasattr(dp.source.type, 'value'): source_type = dp.source.type.value elif dp.source.type: source_type = str(dp.source.type) else: source_type = "unknown" source_desc = dp.source.description if dp.source.description else "N/A" md += f"📌 **Fonte:** {source_desc} ({source_type})\n\n" # Confidenza (gestisce sia float che ConfidenceLevel object) if hasattr(dp.confidence, 'value'): # E' un ConfidenceLevel object confidence_value = dp.confidence.value else: # E' un float diretto - converti in stringa if isinstance(dp.confidence, float): if dp.confidence >= 0.8: confidence_value = "high" elif dp.confidence >= 0.5: confidence_value = "medium" else: confidence_value = "low" else: confidence_value = str(dp.confidence) confidence_emoji = _confidence_emoji(confidence_value) # Aggiungi tooltip di aiuto per spiegare l'affidabilità tooltip_html = ''' ? Cos'è l'Affidabilità? Indica quanto sono attendibili i dati:

Per migliorare l'affidabilitĂ , fornire dati ufficiali del club.
''' md += f"{confidence_emoji} **Affidabilità:** {confidence_value.title()} {tooltip_html}\n\n" md += "---\n\n" # Aggiungi insights se presenti (attributo opzionale) if hasattr(section, 'insights') and section.insights: md += "\n## 💡 Insights Strategici\n\n" for insight in section.insights: md += f"- {insight}\n" md += "\n" return md def _format_value(value: float, unit: str) -> str: """Formatta valore con unit appropriate""" if unit == "EUR": if value >= 1_000_000: return f"€{value/1_000_000:.2f}M" elif value >= 1_000: return f"€{value/1_000:.1f}K" else: return f"€{value:.0f}" elif unit == "%": return f"{value:.1f}%" elif unit in ["anni", "giocatori", "atleti", "squadre", "campi", "posti", "abbonati", "followers", "progetti", "persone", "FTE", "membri"]: return f"{int(value)} {unit}" else: return f"{value} {unit}" def _confidence_emoji(confidence_level: str) -> str: """Emoji per livello di confidenza""" mapping = { "high": "✅", "medium": "⚠️", "low": "❗", "estimated": "📊" } return mapping.get(confidence_level.lower(), "❓")