2026-07-13 18:48:28 +01:00
|
|
|
"""/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(
|
2026-07-14 22:42:10 +01:00
|
|
|
app_module,
|
|
|
|
|
"get_supplementary_data_batch",
|
|
|
|
|
lambda db, urns: {int(u): dict(CANNED_SUPPLEMENTARY) for u in urns},
|
2026-07-13 18:48:28 +01:00
|
|
|
)
|
|
|
|
|
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
|
|
|
|
|
|
2026-07-14 22:42:10 +01:00
|
|
|
def _boom(db, urns):
|
2026-07-13 18:48:28 +01:00
|
|
|
raise RuntimeError("marts unavailable")
|
|
|
|
|
|
2026-07-14 22:42:10 +01:00
|
|
|
monkeypatch.setattr(app_module, "get_supplementary_data_batch", _boom)
|
2026-07-13 18:48:28 +01:00
|
|
|
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
|