Source document

pipeline_philly/verify/completeness.py

Served verbatim from the project repository. Internal working document conventions apply: documents may reference file paths, branch names, and findings-ledger anchors from the repo.

"""(i) Completeness: are years or schools missing from served vs source?

Per Philly metric, compare the set of (school, year) cells in the source file
against the set in the served DB. Reports source-only cells (loader skipped),
served-only cells (where did these come from?), and overall coverage.

Output: docs/qa_reports/philly/__completeness.md

Run: python pipeline_philly/verify/completeness.py
"""
from __future__ import annotations

import json
import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))

import yaml  # noqa: E402

from pipeline_philly.verify import common  # noqa: E402
from pipeline_philly.verify.connectors import served_db  # noqa: E402
from pipeline_philly.verify.connectors import source_file  # noqa: E402
from pipeline_philly.verify.reconcile import read_source  # noqa: E402


def main() -> None:
    common.ensure_qa_dir()
    with open(common.COMPONENTS_YAML) as f:
        components = yaml.safe_load(f)

    rows: list[dict] = []
    for c in components:
        metric = c["metric_key"]
        subgroup = c["subgroup"]
        cut = c["population_cut"]
        served = served_db.fetch(metric, subgroup=subgroup, population_cut=cut)
        source = read_source(c)
        year_filter = c.get("year")
        if year_filter:
            served = {k: v for k, v in served.items() if k[1] == year_filter}
            source = {k: v for k, v in source.items() if k[1] == year_filter}
        sk, fk = set(served.keys()), set(source.keys())
        rows.append({
            "metric": metric,
            "subgroup": subgroup,
            "population_cut": cut,
            "year": year_filter or "all",
            "served_cells": len(sk),
            "source_cells": len(fk),
            "common_cells": len(sk & fk),
            "served_only_cells": len(sk - fk),
            "source_only_cells": len(fk - sk),
            "served_only_examples": sorted([f"{k[0]} {k[1]}" for k in (sk - fk)])[:10],
            "source_only_examples": sorted([f"{k[0]} {k[1]}" for k in (fk - sk)])[:10],
        })

    out: list[str] = []
    out.append("# Completeness (i) — served DB vs ingested source\n")
    out.append("Per-metric coverage: how many (school, year) cells exist in each layer\n")
    out.append("and how many appear in only one of the two.\n")
    out.append("\n| metric | year | served | source | common | served_only | source_only |")
    out.append("|---|---|---:|---:|---:|---:|---:|")
    for r in rows:
        out.append(
            f"| `{r['metric']}` | {r['year']} | "
            f"{r['served_cells']} | {r['source_cells']} | "
            f"{r['common_cells']} | {r['served_only_cells']} | {r['source_only_cells']} |"
        )
    for r in rows:
        if r["served_only_examples"] or r["source_only_examples"]:
            out.append(f"\n## {r['metric']} — uncommon cells")
            if r["source_only_examples"]:
                out.append("### Source-only (loader didn't ingest)")
                for e in r["source_only_examples"]: out.append(f"- {e}")
            if r["served_only_examples"]:
                out.append("### Served-only (in DB but not in source)")
                for e in r["served_only_examples"]: out.append(f"- {e}")

    (common.QA_REPORTS_DIR / "__completeness.md").write_text("\n".join(out))
    (common.QA_REPORTS_DIR / "__completeness.json").write_text(json.dumps(rows, indent=2))

    print(f"completeness report: {len(rows)} components")
    for r in rows:
        print(
            f"  {r['metric']:38s} served={r['served_cells']:>4d} "
            f"source={r['source_cells']:>4d} common={r['common_cells']:>4d} "
            f"served_only={r['served_only_cells']:>3d} source_only={r['source_only_cells']:>3d}"
        )


if __name__ == "__main__":
    main()