2026-07-14 13:05:03 +01:00
|
|
|
"""_national_averages_payload reads persisted marts (computed at import
|
2026-07-16 19:07:49 +01:00
|
|
|
time) — it must never aggregate the dataframe. Both marts hold OFFICIAL
|
|
|
|
|
DfE figures, so a missing KS4 mart yields an empty secondary series —
|
|
|
|
|
never a computed stand-in the UI would mislabel as official."""
|
2026-07-14 13:05:03 +01:00
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
import pandas as pd
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
LATEST = 202425
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _df():
|
|
|
|
|
return pd.DataFrame(
|
|
|
|
|
[
|
|
|
|
|
dict(year=202324, attainment_8_score=40.0, rwm_expected_pct=np.nan),
|
|
|
|
|
dict(year=LATEST, attainment_8_score=50.0, rwm_expected_pct=np.nan),
|
|
|
|
|
dict(year=LATEST, attainment_8_score=30.0, rwm_expected_pct=np.nan),
|
|
|
|
|
dict(year=LATEST, attainment_8_score=np.nan, rwm_expected_pct=80.0),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _Ks2Row:
|
|
|
|
|
year = LATEST
|
|
|
|
|
rwm_expected_pct = 62.1
|
|
|
|
|
gps_expected_pct = 72.0
|
2026-07-21 14:55:43 +01:00
|
|
|
# Per-subject higher-standard nationals — reading/maths reach the higher
|
|
|
|
|
# standard, writing is teacher-assessed at greater depth (writing_gd_pct).
|
|
|
|
|
reading_high_pct = 29.0
|
|
|
|
|
writing_gd_pct = 13.0
|
|
|
|
|
maths_high_pct = 24.0
|
2026-07-14 13:05:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class _Ks4Row:
|
|
|
|
|
year = LATEST
|
|
|
|
|
attainment_8_score = 46.5
|
|
|
|
|
progress_8_score = -0.02
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _StubSession:
|
|
|
|
|
"""Returns KS2 rows for the first query and KS4 rows for the second —
|
|
|
|
|
mirroring the payload's query order."""
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.calls = 0
|
|
|
|
|
|
|
|
|
|
def query(self, model):
|
|
|
|
|
self._model = model.__name__
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def order_by(self, *a):
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def all(self):
|
|
|
|
|
return [_Ks2Row()] if self._model == "Ks2NationalAverage" else [_Ks4Row()]
|
|
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _Ks4MissingSession(_StubSession):
|
|
|
|
|
def all(self):
|
|
|
|
|
if self._model == "Ks4NationalAverage":
|
|
|
|
|
raise RuntimeError("relation does not exist")
|
|
|
|
|
return [_Ks2Row()]
|
|
|
|
|
|
|
|
|
|
def rollback(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture()
|
|
|
|
|
def payload(monkeypatch):
|
|
|
|
|
from backend import app as app_module
|
|
|
|
|
from backend import database as database_module
|
|
|
|
|
|
|
|
|
|
def _run(session_cls):
|
|
|
|
|
monkeypatch.setattr(database_module, "SessionLocal", session_cls)
|
|
|
|
|
return app_module._national_averages_payload(_df())
|
|
|
|
|
|
|
|
|
|
return _run
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_ks4_averages_come_from_the_mart_not_the_dataframe(payload):
|
|
|
|
|
body = payload(_StubSession)
|
|
|
|
|
# Mart value (46.5), NOT the dataframe mean of (50+30)/2 = 40.0
|
|
|
|
|
assert body["secondary"]["attainment_8_score"] == 46.5
|
|
|
|
|
assert body["primary"]["rwm_expected_pct"] == 62.1
|
|
|
|
|
assert body["by_year"][-1]["secondary"]["progress_8_score"] == -0.02
|
|
|
|
|
|
|
|
|
|
|
2026-07-21 14:55:43 +01:00
|
|
|
def test_per_subject_higher_standard_nationals_are_surfaced(payload):
|
|
|
|
|
# The SATs chart compares each bar to its own benchmark, so the per-subject
|
|
|
|
|
# higher-standard / greater-depth nationals must reach the payload — not
|
|
|
|
|
# only the combined rwm_high_pct.
|
|
|
|
|
body = payload(_StubSession)
|
|
|
|
|
assert body["primary"]["reading_high_pct"] == 29.0
|
|
|
|
|
assert body["primary"]["writing_gd_pct"] == 13.0
|
|
|
|
|
assert body["primary"]["maths_high_pct"] == 24.0
|
|
|
|
|
|
|
|
|
|
|
2026-07-16 19:07:49 +01:00
|
|
|
def test_ks4_secondary_empty_when_mart_missing(payload):
|
|
|
|
|
# No computed stand-in: the UI labels national figures as official DfE
|
|
|
|
|
# data, so an empty mart must yield an empty secondary series.
|
2026-07-14 13:05:03 +01:00
|
|
|
body = payload(_Ks4MissingSession)
|
2026-07-16 19:07:49 +01:00
|
|
|
assert body["secondary"] == {}
|
|
|
|
|
assert all(not e["secondary"] for e in body["by_year"])
|
|
|
|
|
# The KS2 series is unaffected.
|
|
|
|
|
assert body["primary"]["rwm_expected_pct"] == 62.1
|