feat(01-02): costanti dominio, schemas Pydantic, FormatSelector
- backend/constants.py: CANVA_FIELDS (33 col locked), PERSUASION_DISTRIBUTION (13), SCHWARTZ_DISTRIBUTION (13) - backend/schemas/calendar.py: CalendarSlot, CalendarRequest, CalendarResponse - backend/schemas/generate.py: SlideContent, GeneratedPost, TopicResult, GenerateRequest, PostResult, GenerateResponse - backend/data/format_mapping.json: matrice 6 tipi x 5 livelli (30 combinazioni) - backend/services/format_selector.py: FormatSelector con select_format e fallback PAS - fix .gitignore: backend/data/prompts/ e format_mapping.json non erano ignorabili
This commit is contained in:
194
backend/constants.py
Normal file
194
backend/constants.py
Normal file
@@ -0,0 +1,194 @@
|
||||
"""Costanti di dominio LOCKED per PostGenerator.
|
||||
|
||||
ATTENZIONE: Queste costanti sono fondamentali per la coerenza del sistema.
|
||||
Non modificare CANVA_FIELDS, PERSUASION_DISTRIBUTION o SCHWARTZ_DISTRIBUTION
|
||||
senza aggiornare anche il CSV builder e tutti i prompt LLM.
|
||||
"""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Configurazione ciclo editoriale
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
POST_PER_CICLO: int = 13
|
||||
"""Numero fisso di post per ciclo editoriale completo."""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Distribuzione Persuasion Nurturing (PN) — 13 slot per ciclo
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
PERSUASION_DISTRIBUTION: dict[str, int] = {
|
||||
"valore": 4,
|
||||
"storytelling": 2,
|
||||
"news": 2,
|
||||
"riprova_sociale": 3,
|
||||
"coinvolgimento": 1,
|
||||
"promozione": 1,
|
||||
}
|
||||
"""Distribuzione tipo_contenuto per ciclo di 13 post.
|
||||
|
||||
Totale: sum(values) == 13
|
||||
"""
|
||||
|
||||
# Verifica a load-time
|
||||
assert sum(PERSUASION_DISTRIBUTION.values()) == POST_PER_CICLO, (
|
||||
f"PERSUASION_DISTRIBUTION deve sommare a {POST_PER_CICLO}, "
|
||||
f"ora somma a {sum(PERSUASION_DISTRIBUTION.values())}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Distribuzione livelli Schwartz — 13 slot per ciclo
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SCHWARTZ_DISTRIBUTION: dict[str, int] = {
|
||||
"L5": 3, # Inconsapevole del problema — storytelling + news
|
||||
"L4": 3, # Consapevole del problema — valore + news
|
||||
"L3": 4, # Consapevole della soluzione — valore + riprova_sociale
|
||||
"L2": 2, # Consapevole del prodotto — riprova_sociale + coinvolgimento
|
||||
"L1": 1, # Pronto all'acquisto — promozione
|
||||
}
|
||||
"""Distribuzione livelli consapevolezza Schwartz per ciclo di 13 post.
|
||||
|
||||
L5+L4 = 6 (fase Attira/Cattura — top of funnel)
|
||||
L3 = 4 (fase Coinvolgi — middle of funnel)
|
||||
L2+L1 = 3 (fase Converti — bottom of funnel)
|
||||
Totale: sum(values) == 13
|
||||
"""
|
||||
|
||||
# Verifica a load-time
|
||||
assert sum(SCHWARTZ_DISTRIBUTION.values()) == POST_PER_CICLO, (
|
||||
f"SCHWARTZ_DISTRIBUTION deve sommare a {POST_PER_CICLO}, "
|
||||
f"ora somma a {sum(SCHWARTZ_DISTRIBUTION.values())}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Colonne CSV per Canva Bulk Create — LOCKED
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
CANVA_FIELDS: list[str] = [
|
||||
# --- Metadati slot (8 colonne) ---
|
||||
"campagna",
|
||||
"fase_campagna",
|
||||
"tipo_contenuto",
|
||||
"formato_narrativo",
|
||||
"funzione",
|
||||
"livello_schwartz",
|
||||
"target_nicchia",
|
||||
"data_pub_suggerita",
|
||||
# --- Cover slide (3 colonne) ---
|
||||
"cover_title",
|
||||
"cover_subtitle",
|
||||
"cover_image_keyword",
|
||||
# --- Slide 2 (3 colonne) ---
|
||||
"s2_headline",
|
||||
"s2_body",
|
||||
"s2_image_keyword",
|
||||
# --- Slide 3 (3 colonne) ---
|
||||
"s3_headline",
|
||||
"s3_body",
|
||||
"s3_image_keyword",
|
||||
# --- Slide 4 (3 colonne) ---
|
||||
"s4_headline",
|
||||
"s4_body",
|
||||
"s4_image_keyword",
|
||||
# --- Slide 5 (3 colonne) ---
|
||||
"s5_headline",
|
||||
"s5_body",
|
||||
"s5_image_keyword",
|
||||
# --- Slide 6 (3 colonne) ---
|
||||
"s6_headline",
|
||||
"s6_body",
|
||||
"s6_image_keyword",
|
||||
# --- Slide 7 (3 colonne) ---
|
||||
"s7_headline",
|
||||
"s7_body",
|
||||
"s7_image_keyword",
|
||||
# --- CTA slide (3 colonne) ---
|
||||
"cta_text",
|
||||
"cta_subtext",
|
||||
"cta_image_keyword",
|
||||
# --- Extra (1 colonna) ---
|
||||
"caption_instagram",
|
||||
]
|
||||
"""Lista ORDINATA di tutte le colonne del CSV per Canva Bulk Create.
|
||||
|
||||
Struttura:
|
||||
- 8 metadati slot
|
||||
- 24 campi slide (8 slide x 3 campi: headline/title, body/subtitle, image_keyword)
|
||||
Nota: image_keyword contiene parole chiave testuali (NON URL).
|
||||
Gli URL Unsplash verranno aggiunti in Phase 4.
|
||||
- 1 caption Instagram
|
||||
Totale: 33 colonne
|
||||
|
||||
LOCKED: Non aggiungere/rimuovere colonne senza aggiornare tutti i prompt LLM
|
||||
e il CSV builder.
|
||||
"""
|
||||
|
||||
# Verifica a load-time
|
||||
_expected_count = 8 + 24 + 1 # 33
|
||||
assert len(CANVA_FIELDS) == _expected_count, (
|
||||
f"CANVA_FIELDS deve avere {_expected_count} elementi, "
|
||||
f"ne ha {len(CANVA_FIELDS)}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Formati narrativi disponibili (7 formati)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
FORMATI_NARRATIVI: list[str] = [
|
||||
"PAS", # Problema → Agitazione → Soluzione
|
||||
"AIDA", # Attenzione → Interesse → Desiderio → Azione
|
||||
"BAB", # Before → After → Bridge
|
||||
"Listicle", # Lista numerata di punti/consigli
|
||||
"Storytelling", # Narrativa emotiva di trasformazione
|
||||
"Dato_Implicazione", # Dato/statistica → Implicazione → Azione
|
||||
"Obiezione_Risposta", # Obiezione comune → Confutazione → Soluzione
|
||||
]
|
||||
"""I 7 formati narrativi supportati per i caroselli Instagram."""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Funzioni contenuto (4 macro-funzioni editoriali)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
FUNZIONI_CONTENUTO: list[str] = [
|
||||
"Intrattenere",
|
||||
"Educare",
|
||||
"Persuadere",
|
||||
"Convertire",
|
||||
]
|
||||
"""Le 4 macro-funzioni editoriali di ogni post."""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fasi campagna (funnel AIDA semplificato)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
FASI_CAMPAGNA: list[str] = [
|
||||
"Attira", # Top of funnel — inconsapevoli (L5)
|
||||
"Cattura", # Upper middle — consapevoli del problema (L4+L3)
|
||||
"Coinvolgi", # Lower middle — consapevoli della soluzione (L3+L2)
|
||||
"Converti", # Bottom of funnel — pronti all'acquisto (L1+L2)
|
||||
]
|
||||
"""Le 4 fasi del funnel di acquisizione clienti."""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Nicchie target predefinite
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
NICCHIE_DEFAULT: list[str] = [
|
||||
"generico",
|
||||
"dentisti",
|
||||
"avvocati",
|
||||
"ecommerce",
|
||||
"local_business",
|
||||
"agenzie",
|
||||
]
|
||||
"""Lista di nicchie target predefinite.
|
||||
|
||||
"generico" è sempre incluso e viene usato per il 50% degli slot.
|
||||
Le altre nicchie vengono ruotate per il restante 50%.
|
||||
"""
|
||||
Reference in New Issue
Block a user