pipeline_miami/verify/survey_source_check.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.
"""Compare regenerated survey composites with the independent raw-workbook reader.
Run: python -m pipeline_miami.verify.survey_source_check
This checks source artifacts without a database; it is not a served-data verdict.
"""
from __future__ import annotations
import csv
import hashlib
import json
import math
from pathlib import Path
import statistics
from pipeline_miami.verify import common
from pipeline_miami.verify.connectors import source_file
def read_csv(path: Path) -> list[dict[str, str]]:
with path.open(newline="") as handle:
return list(csv.DictReader(handle))
def main() -> None:
reference = common.REPO_ROOT / "data/cities/miami/reference"
items = read_csv(reference / "survey_items.csv")
crosswalk = {(r["respondent"], r["year"], r["item_no"]): r for r in items}
results, verified_sources = [], []
for key in source_file._SURVEY_COLUMNS:
path = common.latest_source_path(f"mdcps_climate_survey_{key}")
provenance = json.loads((path.parent / "provenance.json").read_text())
if hashlib.sha256(path.read_bytes()).hexdigest() != provenance["sha256"]:
raise ValueError(f"Source hash does not match its provenance: {key}")
verified_sources.append(key)
respondent, year = key.split("_")
rows = read_csv(reference / f"parsed/survey/mdcps_climate_survey_{key}.csv")
for domain in ("safety", "leadership", "teaching", "resources"):
core = {r["canonical"] for r in items
if r["respondent"] == respondent and r["domain"] == domain and r["core"] == "True"}
scores: dict[str, list[float]] = {}
for row in rows:
values = scores.setdefault(row["school_number"], [])
item = crosswalk[(respondent, year, row["item_no"])]
if row["suppressed"] == "True" or item["domain"] != domain or item["core"] != "True":
continue
selected = row["pct_disagree"] if item["reverse"] == "True" else row["pct_agree"]
if selected:
value = float(selected)
if math.isfinite(value) and 0 <= value <= 100:
values.append(value)
actual = source_file.climate_survey(year, respondent, domain)
mismatches = []
for (school, _), observed in actual.items():
values = scores[school]
expected = statistics.mean(values) if len(values) * 2 >= len(core) else None
missing_differs = (expected is None) != (observed["value"] is None)
value_differs = (expected is not None and observed["value"] is not None
and abs(expected - observed["value"]) > 1e-6)
if missing_differs or value_differs:
mismatches.append((school, expected, observed))
results.append(dict(key=key, domain=domain, schools=len(actual), mismatches=len(mismatches)))
print(key, domain, len(actual), "schools; mismatches", len(mismatches), flush=True)
if mismatches:
raise ValueError(f"{key} {domain}: {mismatches[:3]}")
common.ensure_qa_dir()
output = common.QA_REPORTS_DIR / "survey_favorable_response_source_check.json"
output.write_text(json.dumps({
"scoring_version": "favorable-response-v2",
"source_sha256_verified": len(verified_sources),
"checks": results,
"note": "Independent raw workbook reader compared with regenerated parsed CSVs and the declared favorable scoring, without a served database.",
}, indent=2) + "\n")
if __name__ == "__main__":
main()