"""tht formula -- concept->SQL formula evidence store (spec D14b, ยง4.7.2). Authoring + listing of reusable concept formulas. Retrieval for the workflow is via `tht search find --kind formula ""`. The reviewer approves/rejects a candidate formula in F4 (decisions concept_formula_approved / concept_formula_rejected), and the approved one is reflected in schema_linking.json (SchemaLinking.concept_formulas). """ from pathlib import Path import typer from tht.cli.config_cmd import CONFIG_OPT from tht.cli.evidence_cmd import evidence_root from tht.cli.schema_cmd import _load_config_or_exit formula_app = typer.Typer(help="Formule di concetto (concept -> SQL) riusabili (D14b)") @formula_app.command("save") def save_cmd( concept: str = typer.Option(..., "--concept", help="Il concetto, es. 'fascia pediatrica'."), sql_file: Path = typer.Option( None, "--sql-file", help="File con l'espressione SQL della formula." ), sql: str = typer.Option(None, "--sql", help="Espressione SQL inline (alternativa a --sql-file)."), column: list[str] = typer.Option(None, "--column", help="Colonna usata (ripetibile)."), status: str = typer.Option("draft", "--status", help="auto | draft | reviewed."), source: list[str] = typer.Option(None, "--source", help="Fonte/evidence (ripetibile)."), config: Path = CONFIG_OPT, ) -> None: """Salva una formula di concetto sotto artifacts/evidence/formulas/.""" from tht.evidence.formula_store import ConceptFormula, save_formula cfg = _load_config_or_exit(config) if sql_file is None and not sql: typer.secho("ERRORE: indica --sql-file oppure --sql \"\".", fg=typer.colors.RED, err=True) raise typer.Exit(code=1) body = sql_file.read_text() if sql_file else sql try: formula = ConceptFormula( concept=concept, columns=column or [], sql=body.strip(), status=status, sources=source or [], ) except ValueError as e: typer.secho(f"ERRORE: formula non valida: {e}", fg=typer.colors.RED, err=True) raise typer.Exit(code=1) path = save_formula(evidence_root(cfg), formula) typer.secho(f"OK: formula '{concept}' salvata in {path}.", fg=typer.colors.GREEN) @formula_app.command("list") def list_cmd( concept: str = typer.Option(None, "--concept", help="Filtra per concetto (substring)."), json_out: bool = typer.Option(False, "--json"), config: Path = CONFIG_OPT, ) -> None: """Elenca le formule salvate (tutte, o filtrate per concetto).""" import json as _json from rich.console import Console from rich.table import Table from tht.evidence.formula_store import _load_all, search_formulas cfg = _load_config_or_exit(config) root = evidence_root(cfg) formulas = search_formulas(root, concept) if concept else _load_all(root) if json_out: typer.echo(_json.dumps([f.model_dump(mode="json") for f in formulas], ensure_ascii=False, indent=2)) return if not formulas: typer.secho("Nessuna formula salvata.", fg=typer.colors.YELLOW) return t = Table(title="Formule di concetto") for col in ("Concetto", "Status", "Colonne"): t.add_column(col) for f in formulas: t.add_row(f.concept, f.status, ", ".join(f.columns)) Console().print(t)