- 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
92 lines
3.2 KiB
Python
92 lines
3.2 KiB
Python
"""FormatSelector — mappa tipo_contenuto x livello_schwartz -> formato_narrativo.
|
|
|
|
Carica la matrice di mapping da format_mapping.json e seleziona il formato
|
|
narrativo più efficace per ogni combinazione di tipo e livello.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
|
|
# Percorso default al file JSON (relativo a questo modulo)
|
|
_DEFAULT_MAPPING_PATH = Path(__file__).parent.parent / "data" / "format_mapping.json"
|
|
|
|
# Fallback se la combinazione non è presente nella matrice
|
|
_FALLBACK_FORMAT = "PAS"
|
|
|
|
|
|
class FormatSelector:
|
|
"""Seleziona il formato narrativo ottimale per un dato tipo_contenuto e livello_schwartz.
|
|
|
|
Carica la matrice di mapping da un file JSON e la mantiene in memoria.
|
|
La selezione è deterministica e basata sulla tabella (nessuna logica LLM).
|
|
|
|
Esempio:
|
|
selector = FormatSelector()
|
|
formato = selector.select_format("valore", "L4") # -> "PAS"
|
|
formato = selector.select_format("storytelling", "L5") # -> "BAB"
|
|
"""
|
|
|
|
def __init__(self, mapping_path: Path | None = None) -> None:
|
|
"""Carica il mapping da file JSON.
|
|
|
|
Args:
|
|
mapping_path: Percorso al file format_mapping.json.
|
|
Default: backend/data/format_mapping.json
|
|
"""
|
|
path = mapping_path or _DEFAULT_MAPPING_PATH
|
|
if not path.exists():
|
|
raise FileNotFoundError(
|
|
f"File format_mapping.json non trovato: {path}. "
|
|
"Assicurati che backend/data/format_mapping.json esista."
|
|
)
|
|
|
|
with path.open(encoding="utf-8") as f:
|
|
raw = json.load(f)
|
|
|
|
# Filtra i commenti (chiavi che iniziano con "_")
|
|
self._mapping: dict[str, dict[str, str]] = {
|
|
k: v for k, v in raw.items() if not k.startswith("_")
|
|
}
|
|
|
|
def select_format(self, tipo_contenuto: str, livello_schwartz: str) -> str:
|
|
"""Ritorna il formato narrativo per la combinazione data.
|
|
|
|
Args:
|
|
tipo_contenuto: Tipo PN (es. "valore", "storytelling", "promozione")
|
|
livello_schwartz: Livello consapevolezza (es. "L1", "L3", "L5")
|
|
|
|
Returns:
|
|
Nome del formato narrativo (es. "PAS", "BAB", "AIDA").
|
|
Ritorna "PAS" come fallback se la combinazione non è nella matrice.
|
|
"""
|
|
tipo_map = self._mapping.get(tipo_contenuto)
|
|
if tipo_map is None:
|
|
return _FALLBACK_FORMAT
|
|
|
|
return tipo_map.get(livello_schwartz, _FALLBACK_FORMAT)
|
|
|
|
def get_mapping(self) -> dict[str, dict[str, str]]:
|
|
"""Ritorna la tabella di mapping completa.
|
|
|
|
Returns:
|
|
Dizionario { tipo_contenuto: { livello_schwartz: formato_narrativo } }
|
|
"""
|
|
return dict(self._mapping)
|
|
|
|
def get_supported_types(self) -> list[str]:
|
|
"""Ritorna la lista dei tipi_contenuto supportati dalla matrice."""
|
|
return list(self._mapping.keys())
|
|
|
|
def get_supported_levels(self) -> list[str]:
|
|
"""Ritorna la lista dei livelli_schwartz supportati dalla matrice.
|
|
|
|
Inferisce i livelli dal primo tipo disponibile (la matrice è consistente).
|
|
"""
|
|
if not self._mapping:
|
|
return []
|
|
first_type = next(iter(self._mapping.values()))
|
|
return sorted(first_type.keys())
|