refactor(harness): route workflow persistence through repositories
This commit is contained in:
+50
-33
@@ -6,7 +6,7 @@ import typer
|
||||
|
||||
from tht.cli.config_cmd import CONFIG_OPT
|
||||
from tht.cli.schema_cmd import _load_config_or_exit
|
||||
from tht.cli.session_cmd import load_session_or_exit, session_dir
|
||||
from tht.cli.session_cmd import load_session_or_exit, load_snapshot_or_exit, session_repository
|
||||
from tht.cli.sql_cmd import promoted_tables_for, require_action
|
||||
|
||||
_RULE6_HINT = (
|
||||
@@ -17,6 +17,27 @@ _RULE6_HINT = (
|
||||
cte_app = typer.Typer(help="Test controllato dei CTE proposti (Agent View Generation)")
|
||||
|
||||
|
||||
@cte_app.command("save")
|
||||
def save_cmd(
|
||||
session: str = typer.Option(..., "--session"),
|
||||
name: str = typer.Option(..., "--name"),
|
||||
file: str = typer.Option(..., "--file", help="File SQL, oppure '-' per stdin."),
|
||||
config: Path = CONFIG_OPT,
|
||||
) -> None:
|
||||
"""Persist one CTE SQL block through the configured session repository."""
|
||||
import sys
|
||||
|
||||
cfg = _load_config_or_exit(config)
|
||||
load_session_or_exit(cfg, session)
|
||||
raw = sys.stdin.read() if file == "-" else Path(file).read_text()
|
||||
try:
|
||||
session_repository(cfg).write_artifact(session, f"cte_sql:{name}", raw)
|
||||
except ValueError as exc:
|
||||
typer.secho(f"ERRORE: {exc}", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(code=1) from None
|
||||
typer.secho(f"OK: CTE {name} salvato.", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
@cte_app.command("test")
|
||||
def test_cmd(
|
||||
name: str = typer.Argument(..., help="Nome del CTE (file sessions/<id>/ctes/<nome>.sql)."),
|
||||
@@ -33,7 +54,7 @@ def test_cmd(
|
||||
CteError,
|
||||
CteTestRecord,
|
||||
_jsonable,
|
||||
append_cte_test,
|
||||
append_cte_test_snapshot,
|
||||
build_test_sql,
|
||||
has_trailing_select,
|
||||
)
|
||||
@@ -43,13 +64,13 @@ def test_cmd(
|
||||
cfg = _load_config_or_exit(config)
|
||||
require_action(cfg, "cte_test")
|
||||
load_session_or_exit(cfg, session)
|
||||
sdir = session_dir(cfg, session)
|
||||
snapshot = load_snapshot_or_exit(cfg, session)
|
||||
|
||||
from tht.cli.phase_cmd import require_phase_or_exit
|
||||
from tht.phase import next_cte
|
||||
|
||||
require_phase_or_exit(cfg, session, 6)
|
||||
nxt = next_cte(sdir)
|
||||
nxt = next_cte(snapshot)
|
||||
if nxt is not None and name != nxt:
|
||||
typer.secho(
|
||||
f"ERRORE: ordine CTE. Ora tocca a '{nxt}' (il primo CTE del piano non "
|
||||
@@ -60,11 +81,10 @@ def test_cmd(
|
||||
)
|
||||
raise typer.Exit(code=5)
|
||||
|
||||
cte_file = sdir / "ctes" / f"{name}.sql"
|
||||
if not cte_file.exists():
|
||||
typer.secho(f"ERRORE: file CTE non trovato: {cte_file}", fg=typer.colors.RED, err=True)
|
||||
cte_sql = snapshot.artifacts.get(f"cte_sql:{name}")
|
||||
if cte_sql is None:
|
||||
typer.secho(f"ERRORE: file CTE non trovato: {name}", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(code=1)
|
||||
cte_sql = cte_file.read_text()
|
||||
sql_hash = hashlib.sha256(cte_sql.encode()).hexdigest()
|
||||
|
||||
def _record_error(message: str) -> None:
|
||||
@@ -72,7 +92,7 @@ def test_cmd(
|
||||
name=name, ts=datetime.now(UTC), sql_hash=sql_hash,
|
||||
status="error", error=message,
|
||||
)
|
||||
append_cte_test(sdir, record)
|
||||
append_cte_test_snapshot(session_repository(cfg), snapshot, record)
|
||||
if json_out:
|
||||
typer.echo(record.model_dump_json())
|
||||
else:
|
||||
@@ -118,7 +138,7 @@ def test_cmd(
|
||||
execution_ms=result.execution_ms, warnings=warnings,
|
||||
preview_rows=[[_jsonable(cell) for cell in row] for row in result.rows],
|
||||
)
|
||||
append_cte_test(sdir, record)
|
||||
append_cte_test_snapshot(session_repository(cfg), snapshot, record)
|
||||
|
||||
if json_out:
|
||||
typer.echo(record.model_dump_json())
|
||||
@@ -133,7 +153,7 @@ def test_cmd(
|
||||
Console().print(table)
|
||||
for w in warnings:
|
||||
typer.secho(f" warning: {w}", fg=typer.colors.YELLOW)
|
||||
typer.secho(f"OK: esito registrato in {sdir / 'cte_tests.json'}", fg=typer.colors.GREEN)
|
||||
typer.secho("OK: esito registrato in cte_tests.json", fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
CTE_PLAN_DOC_FILE = "cte_plan_doc.json"
|
||||
@@ -156,13 +176,10 @@ def plan_cmd(
|
||||
import json
|
||||
import sys
|
||||
|
||||
from tht.phase import CTE_PLAN_FILE
|
||||
|
||||
cfg = _load_config_or_exit(config)
|
||||
load_session_or_exit(cfg, session)
|
||||
from tht.cli.phase_cmd import require_phase_or_exit
|
||||
require_phase_or_exit(cfg, session, 6)
|
||||
sdir = session_dir(cfg, session)
|
||||
|
||||
doc_data = None
|
||||
if doc is not None:
|
||||
@@ -180,11 +197,11 @@ def plan_cmd(
|
||||
)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
sdir.mkdir(parents=True, exist_ok=True)
|
||||
(sdir / CTE_PLAN_FILE).write_text(json.dumps(name, ensure_ascii=False))
|
||||
repository = session_repository(cfg)
|
||||
repository.write_artifact(session, "cte_plan", json.dumps(name, ensure_ascii=False))
|
||||
if doc_data is not None:
|
||||
(sdir / CTE_PLAN_DOC_FILE).write_text(json.dumps(doc_data, ensure_ascii=False))
|
||||
typer.secho(f"OK: piano CTE salvato ({len(name)} CTE) in {sdir / CTE_PLAN_FILE}.",
|
||||
repository.write_artifact(session, "cte_plan_doc", json.dumps(doc_data, ensure_ascii=False))
|
||||
typer.secho(f"OK: piano CTE salvato ({len(name)} CTE).",
|
||||
fg=typer.colors.GREEN)
|
||||
|
||||
|
||||
@@ -201,7 +218,7 @@ def next_cmd(
|
||||
|
||||
cfg = _load_config_or_exit(config)
|
||||
load_session_or_exit(cfg, session)
|
||||
nxt = next_cte(session_dir(cfg, session))
|
||||
nxt = next_cte(load_snapshot_or_exit(cfg, session))
|
||||
if nxt:
|
||||
typer.echo(nxt)
|
||||
|
||||
@@ -222,9 +239,9 @@ def info_cmd(
|
||||
|
||||
cfg = _load_config_or_exit(config)
|
||||
load_session_or_exit(cfg, session)
|
||||
sdir = session_dir(cfg, session)
|
||||
snapshot = load_snapshot_or_exit(cfg, session)
|
||||
|
||||
plan = cte_plan(sdir)
|
||||
plan = cte_plan(snapshot)
|
||||
if not plan:
|
||||
typer.secho(f"ERRORE: {CTE_PLAN_FILE} assente o vuoto per la sessione '{session}'.",
|
||||
fg=typer.colors.RED, err=True)
|
||||
@@ -234,24 +251,24 @@ def info_cmd(
|
||||
fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
cte_file = sdir / "ctes" / f"{name}.sql"
|
||||
if not cte_file.exists():
|
||||
typer.secho(f"ERRORE: file CTE non trovato: {cte_file}", fg=typer.colors.RED, err=True)
|
||||
cte_sql = snapshot.artifacts.get(f"cte_sql:{name}")
|
||||
if cte_sql is None:
|
||||
typer.secho(f"ERRORE: file CTE non trovato: {name}", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
doc = None
|
||||
doc_path = sdir / CTE_PLAN_DOC_FILE
|
||||
if doc_path.exists():
|
||||
full_doc = _json.loads(doc_path.read_text())
|
||||
raw_doc = snapshot.artifacts.get("cte_plan_doc")
|
||||
if raw_doc:
|
||||
full_doc = _json.loads(raw_doc)
|
||||
for c in full_doc.get("ctes", []):
|
||||
if c.get("name") == name:
|
||||
doc = {k: v for k, v in c.items() if k != "name"}
|
||||
break
|
||||
|
||||
from tht.ctetest import CteError, load_cte_tests
|
||||
from tht.ctetest import CteError, load_cte_tests_text
|
||||
|
||||
try:
|
||||
records = [r for r in load_cte_tests(sdir) if r.name == name]
|
||||
records = [r for r in load_cte_tests_text(snapshot.artifacts.get("cte_tests", "")) if r.name == name]
|
||||
except CteError as e:
|
||||
typer.secho(f"ERRORE: impossibile leggere cte_tests.json: {e}",
|
||||
fg=typer.colors.RED, err=True)
|
||||
@@ -263,8 +280,8 @@ def info_cmd(
|
||||
"index": plan.index(name) + 1,
|
||||
"total": len(plan),
|
||||
"plan": plan,
|
||||
"sql": cte_file.read_text(),
|
||||
"approved": name in approved_ctes(sdir),
|
||||
"sql": cte_sql,
|
||||
"approved": name in approved_ctes(snapshot),
|
||||
"doc": doc,
|
||||
"last_test": last_test,
|
||||
}
|
||||
@@ -282,12 +299,12 @@ def list_cmd(
|
||||
config: Path = CONFIG_OPT,
|
||||
) -> None:
|
||||
"""Ultimo esito registrato per ogni CTE della sessione."""
|
||||
from tht.ctetest import CteError, load_cte_tests
|
||||
from tht.ctetest import CteError, load_cte_tests_text
|
||||
|
||||
cfg = _load_config_or_exit(config)
|
||||
load_session_or_exit(cfg, session)
|
||||
try:
|
||||
records = load_cte_tests(session_dir(cfg, session))
|
||||
records = load_cte_tests_text(load_snapshot_or_exit(cfg, session).artifacts.get("cte_tests", ""))
|
||||
except CteError as e:
|
||||
typer.secho(f"ERRORE: impossibile leggere cte_tests.json: {e}",
|
||||
fg=typer.colors.RED, err=True)
|
||||
|
||||
Reference in New Issue
Block a user