"""CalendarService — genera il calendario editoriale con awareness levels (Schwartz). Versione adattata per Leopost Full (standalone, senza dipendenze da postgenerator). Genera un piano di pubblicazione con: - Formati narrativi: PAS, AIDA, BAB, Storytelling, Listicle, Dato_Implicazione - Awareness levels (Schwartz): 1-Unaware, 2-Problem Aware, 3-Solution Aware, 4-Product Aware, 5-Most Aware - Date di pubblicazione suggerite """ from __future__ import annotations from datetime import date, timedelta from typing import Optional # --------------------------------------------------------------------------- # Costanti # --------------------------------------------------------------------------- FORMATI_NARRATIVI = [ "PAS", "AIDA", "BAB", "Storytelling", "Listicle", "Dato_Implicazione", ] AWARENESS_LEVELS = { 1: "Unaware", 2: "Problem Aware", 3: "Solution Aware", 4: "Product Aware", 5: "Most Aware", } # Mapping formato narrativo -> awareness levels consigliati _FORMATO_TO_LEVELS: dict[str, list[int]] = { "PAS": [2, 3], "AIDA": [3, 4, 5], "BAB": [2, 3], "Storytelling": [1, 2], "Listicle": [2, 3], "Dato_Implicazione": [1, 2, 3], } # Distribuzione default per generazione automatica _DEFAULT_DISTRIBUTION = [ ("Storytelling", 1), ("Dato_Implicazione", 2), ("PAS", 2), ("Listicle", 3), ("AIDA", 4), ("BAB", 3), ("AIDA", 5), ] # --------------------------------------------------------------------------- # CalendarService # --------------------------------------------------------------------------- class CalendarService: """Genera il calendario editoriale con awareness levels e formati narrativi.""" def generate_calendar( self, topics: list[str], num_posts: int = 7, format_narrativo: Optional[str] = None, awareness_level: Optional[int] = None, start_date: Optional[str] = None, ) -> list[dict]: """Genera un calendario editoriale.""" if start_date: try: data_inizio = date.fromisoformat(start_date) except ValueError: data_inizio = date.today() else: data_inizio = date.today() dates = self._generate_dates(data_inizio, num_posts) if format_narrativo and awareness_level: distribution = [(format_narrativo, awareness_level)] * num_posts elif format_narrativo: levels = _FORMATO_TO_LEVELS.get(format_narrativo, [2, 3, 4]) distribution = [ (format_narrativo, levels[i % len(levels)]) for i in range(num_posts) ] elif awareness_level: compatible_formats = [ fmt for fmt, levels in _FORMATO_TO_LEVELS.items() if awareness_level in levels ] if not compatible_formats: compatible_formats = FORMATI_NARRATIVI distribution = [ (compatible_formats[i % len(compatible_formats)], awareness_level) for i in range(num_posts) ] else: distribution = [ _DEFAULT_DISTRIBUTION[i % len(_DEFAULT_DISTRIBUTION)] for i in range(num_posts) ] slots = [] for i in range(num_posts): topic = topics[i % len(topics)] if topics else f"Topic {i + 1}" fmt, level = distribution[i] slots.append({ "indice": i, "topic": topic, "formato_narrativo": fmt, "awareness_level": level, "awareness_label": AWARENESS_LEVELS.get(level, f"Level {level}"), "data_pubblicazione": dates[i].isoformat(), "note": self._generate_note(fmt, level), }) return slots def get_formats(self) -> list[dict]: """Ritorna la lista dei formati narrativi disponibili.""" return [ { "value": fmt, "label": fmt.replace("_", " "), "awareness_levels": _FORMATO_TO_LEVELS.get(fmt, [2, 3]), } for fmt in FORMATI_NARRATIVI ] @staticmethod def _generate_dates(start: date, count: int) -> list[date]: """Genera date di pubblicazione (lun, mer, ven).""" publish_days = [0, 2, 4] dates = [] current = start while current.weekday() not in publish_days: current += timedelta(days=1) while len(dates) < count: if current.weekday() in publish_days: dates.append(current) current += timedelta(days=1) return dates @staticmethod def _generate_note(formato: str, level: int) -> str: """Genera una nota descrittiva per lo slot.""" level_label = AWARENESS_LEVELS.get(level, f"L{level}") notes = { "PAS": f"Problema-Agitazione-Soluzione. Target: {level_label}", "AIDA": f"Attenzione-Interesse-Desiderio-Azione. Target: {level_label}", "BAB": f"Before-After-Bridge. Target: {level_label}", "Storytelling": f"Racconta una storia autentica. Target: {level_label}", "Listicle": f"Lista di punti pratici. Target: {level_label}", "Dato_Implicazione": f"Dato sorprendente + implicazione. Target: {level_label}", } return notes.get(formato, f"Formato {formato}. Target: {level_label}")