feat(api): compare endpoint carries supplementary blocks, national averages and benchmarks
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 9m45s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 22s
PR Checks / Build Frontend (no push) (pull_request) Successful in 51s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 37s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 3m12s
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 9m45s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 22s
PR Checks / Build Frontend (no push) (pull_request) Successful in 51s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 37s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 3m12s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
"""/api/compare enrichment for the compare redesign: per-school
|
||||
supplementary blocks, top-level national_averages (shared with the
|
||||
/api/national-averages endpoint) and computed benchmarks — all additive."""
|
||||
|
||||
import types
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
LATEST = 202425
|
||||
|
||||
CANNED_SUPPLEMENTARY = {
|
||||
"ofsted": {"overall_effectiveness": 2, "grade_source": "graded",
|
||||
"report_card": {}, "ofsted_page_url": "https://reports.ofsted.gov.uk/provider/21/100140"},
|
||||
"census": {"year": 202526, "fsm_pct": 29.8},
|
||||
"admissions": {"year": 202627, "second_preference_offers": 4},
|
||||
"admissions_history": [{"year": 202627, "second_preference_offers": 4}],
|
||||
"sen_detail": None,
|
||||
"phonics": None,
|
||||
"deprivation": {"idaci_decile": 4},
|
||||
"finance": None,
|
||||
}
|
||||
|
||||
|
||||
def _two_primary_schools_df() -> pd.DataFrame:
|
||||
rows = []
|
||||
for urn, name, rwm, dis in ((100140, "Plumcroft Primary School", 79.0, 72.0),
|
||||
(138690, "Barclay Primary School", 87.0, 86.0)):
|
||||
rows.append(dict(
|
||||
urn=urn, school_name=name, local_authority="Greenwich",
|
||||
school_type="Community school", address="1 Road", phase="Primary",
|
||||
year=LATEST, rwm_expected_pct=rwm, attainment_8_score=np.nan,
|
||||
eligible_pupils=60, rwm_expected_disadvantaged_pct=dis,
|
||||
eal_pct=20.0, sen_support_pct=14.0, disadvantaged_pct=25.0,
|
||||
total_pupils=1000.0,
|
||||
))
|
||||
return pd.DataFrame(rows)
|
||||
|
||||
|
||||
class _StubNatRow:
|
||||
year = 202425
|
||||
rwm_expected_pct = 62.1
|
||||
gps_expected_pct = 72.0
|
||||
science_expected_pct = 81.0
|
||||
|
||||
|
||||
class _StubSession:
|
||||
def query(self, *a, **k):
|
||||
return self
|
||||
|
||||
def order_by(self, *a, **k):
|
||||
return self
|
||||
|
||||
def all(self):
|
||||
return [_StubNatRow()]
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client(monkeypatch):
|
||||
from backend import app as app_module
|
||||
from backend import database as database_module
|
||||
|
||||
monkeypatch.setattr(app_module, "load_school_data", _two_primary_schools_df)
|
||||
monkeypatch.setattr(
|
||||
app_module, "get_supplementary_data", lambda db, urn: dict(CANNED_SUPPLEMENTARY)
|
||||
)
|
||||
monkeypatch.setattr(database_module, "SessionLocal", _StubSession)
|
||||
return TestClient(app_module.app, raise_server_exceptions=False)
|
||||
|
||||
|
||||
def test_existing_shape_is_preserved(client):
|
||||
body = client.get("/api/compare?urns=100140,138690").json()
|
||||
school = body["comparison"]["100140"]
|
||||
assert school["school_info"]["rwm_expected_pct"] == 79.0
|
||||
assert school["yearly_data"][0]["year"] == LATEST
|
||||
|
||||
|
||||
def test_each_school_gains_supplementary_blocks(client):
|
||||
body = client.get("/api/compare?urns=100140,138690").json()
|
||||
for urn in ("100140", "138690"):
|
||||
school = body["comparison"][urn]
|
||||
assert school["ofsted"]["grade_source"] == "graded"
|
||||
assert school["census"]["fsm_pct"] == 29.8
|
||||
assert school["admissions"]["second_preference_offers"] == 4
|
||||
assert school["admissions_history"][0]["year"] == 202627
|
||||
assert school["deprivation"]["idaci_decile"] == 4
|
||||
|
||||
|
||||
def test_top_level_national_averages_and_benchmarks(client):
|
||||
body = client.get("/api/compare?urns=100140,138690").json()
|
||||
assert body["national_averages"]["year"] == LATEST
|
||||
assert body["benchmarks"]["source"] == "state-school average (computed from our dataset)"
|
||||
# weighted over equal cohorts of 72 and 86 = 79.0
|
||||
assert body["benchmarks"]["primary"]["disadvantaged_rwm_expected_pct"] == 79.0
|
||||
|
||||
|
||||
def test_supplementary_failure_degrades_not_500(client, monkeypatch):
|
||||
from backend import app as app_module
|
||||
|
||||
def _boom(db, urn):
|
||||
raise RuntimeError("marts unavailable")
|
||||
|
||||
monkeypatch.setattr(app_module, "get_supplementary_data", _boom)
|
||||
resp = client.get("/api/compare?urns=100140")
|
||||
assert resp.status_code == 200
|
||||
school = resp.json()["comparison"]["100140"]
|
||||
assert school["ofsted"] is None
|
||||
assert school["admissions_history"] == []
|
||||
|
||||
|
||||
def test_national_averages_endpoint_exposes_gps_science(client):
|
||||
body = client.get("/api/national-averages").json()
|
||||
latest_primary_by_year = [e["primary"] for e in body["by_year"] if e["primary"]]
|
||||
assert latest_primary_by_year, "expected official by_year rows from the stub"
|
||||
assert latest_primary_by_year[-1]["gps_expected_pct"] == 72.0
|
||||
assert latest_primary_by_year[-1]["science_expected_pct"] == 81.0
|
||||
Reference in New Issue
Block a user