Change only starter/report_tool.py. Standard library only; no new
dependencies.
load_records(path) -> list[dict]:.csvfiles load throughcsv.DictReader..jsonfiles load as a list of objects; a non-list or non-object payload raisesValueError.- Any other suffix raises
ValueErrornaming the unsupported file.
build_report(records, *, required_fields, numeric_field, group_field) -> dict:- Returns
{"total", "valid", "invalid", "groups", "errors"}. - Invalid rows never abort the run: each is collected in
errorsas{"index": <row position>, "reason": <short string>}. Invalid means: not a dict, missing or blank required field, or a non-numericnumeric_fieldvalue (booleans do not count as numbers). - Valid rows aggregate into
groups[group_value] = {"count": int, "total": float}; each group total is rounded to two decimals. total = valid + invalidmust hold for every input.
- Returns
write_report(report, destination):- Writes JSON (UTF-8, indent 2, trailing newline) atomically: write a
sibling temp file, then
os.replace. - Creates missing parent directories.
- Never leaves a
.tmpfile behind on success.
- Writes JSON (UTF-8, indent 2, trailing newline) atomically: write a
sibling temp file, then
run_scenario(scenario_dir) -> dict:- Reads
scenario.json(data_file,required_fields,numeric_field,group_field,report_file), processes the data file, writes the report inside the scenario directory, and returns the report.
- Reads
main(argv=None) -> int:- Exactly one argument (the scenario directory). Otherwise print usage to stderr and return 2.
- On success print
total=... valid=... invalid=...to stdout and return 0. - On input failure print
error: ...to stderr and return 1.
Done means python verify.py starter exits 0 with all nine tests passing,
and python verify.py starter --expect-failure exits nonzero because the
starter no longer reproduces the unfinished state.