Initial commit: Leopost Full — merge di Leopost, Post Generator e Autopilot OS

- Backend FastAPI con multi-LLM (Claude/OpenAI/Gemini)
- Publishing su Facebook, Instagram, YouTube, TikTok
- Calendario editoriale con awareness levels (PAS, AIDA, BAB...)
- Design system Editorial Fresh (Fraunces + DM Sans)
- Scheduler automatico, gestione commenti AI, affiliate links

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Michele
2026-03-31 17:23:16 +02:00
commit 519a580679
58 changed files with 8348 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
"""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}")