8 moduli leaf portati verbatim da ChironeWp3 con rename psdwp3→tht: - vendor/thoth_lsh (MinHash/LSH, leaf puro datasketch+tqdm) + VENDORED.md - lshindex/ (build/save/load/query, dipende vendor + LshConfig) - sqlcheck/ (validate_sql, leaf ExecutionConfig+mschema) - execute/ + execute/warnings (run_controlled/explain, leaf sqlglot+sqlalchemy) - rest/execute + rest/explain (REST variants, dipendono execute+rest.client) - ctetest (CTE test records, leaf sqlglot+pydantic) - report (validation report rendering, dipende execute+sqlcheck) - datamart (stub NotImplementedError) Verifica: import smoke catena completa OK, pytest 109 passed. Deps (datasketch, sqlglot, sqlalchemy, pydantic, requests, tqdm) già in pyproject. VENDORED.md neutralizzato (riferimenti PsdWp3→Thoth).
75 lines
2.2 KiB
Python
75 lines
2.2 KiB
Python
from tht.execute import ExecResult, PlanSummary
|
|
from tht.sqlcheck import CheckResult
|
|
|
|
NOTE_HEADING = "## Note del reviewer"
|
|
|
|
|
|
def _markdown_table(columns: list[str], rows: list[tuple]) -> str:
|
|
lines = [
|
|
"| " + " | ".join(columns) + " |",
|
|
"|" + "---|" * len(columns),
|
|
]
|
|
for row in rows:
|
|
lines.append("| " + " | ".join(str(v) for v in row) + " |")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def render_validation_report(
|
|
*,
|
|
session_id: str,
|
|
check: CheckResult,
|
|
plan: PlanSummary,
|
|
plan_warnings: list[str],
|
|
result: ExecResult,
|
|
runtime_warnings: list[str],
|
|
static_warnings: list[str],
|
|
limit: int,
|
|
reviewer_notes: str = "",
|
|
) -> str:
|
|
"""Sezioni fattuali del validation report. La sezione note e' di Pi/reviewer:
|
|
se gia' scritta, va passata in reviewer_notes per preservarla."""
|
|
|
|
def warn(ws: list[str]) -> str:
|
|
return "\n".join(f"- warning: {w}" for w in ws) if ws else "- nessun warning"
|
|
|
|
parts = [
|
|
f"# Validation report — {session_id}",
|
|
"",
|
|
"> Avvertenza: questo report contiene sample di dati del database.",
|
|
"",
|
|
"## Parsing e read-only",
|
|
"",
|
|
"- parsing: OK" if check.ok else "- parsing: ERRORI",
|
|
"- statement read-only per struttura: OK" if check.ok else
|
|
"\n".join(f"- ERRORE: {e}" for e in check.errors),
|
|
warn(check.warnings + static_warnings),
|
|
"",
|
|
"## Piano di esecuzione",
|
|
"",
|
|
f"- costo totale stimato: {plan.total_cost}",
|
|
f"- righe stimate: {plan.plan_rows}",
|
|
f"- nodi: {', '.join(plan.node_types)}",
|
|
warn(plan_warnings),
|
|
"",
|
|
"## Preview / aggregato",
|
|
"",
|
|
f"- tempo di esecuzione: {result.execution_ms} ms (limit {limit})",
|
|
"",
|
|
_markdown_table(result.columns, result.rows),
|
|
"",
|
|
warn(runtime_warnings),
|
|
"",
|
|
NOTE_HEADING,
|
|
"",
|
|
reviewer_notes.strip(),
|
|
"",
|
|
]
|
|
return "\n".join(parts)
|
|
|
|
|
|
def extract_reviewer_notes(report_md: str) -> str:
|
|
"""Estrae il contenuto della sezione note da un report esistente."""
|
|
if NOTE_HEADING not in report_md:
|
|
return ""
|
|
return report_md.split(NOTE_HEADING, 1)[1].strip()
|