120 lines
4.1 KiB
Python
120 lines
4.1 KiB
Python
"""/api/la-averages serves DfE's own LA averages (fact_ks4_la_averages).
|
|
|
|
It used to average the dataframe: every school with an Attainment 8,
|
|
independent and special schools included. Kensington and Chelsea came out at
|
|
35.2 against DfE's 54.5, and most LAs about 7 points low (audit H2).
|
|
"""
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
LATEST = 202425
|
|
|
|
|
|
def _df():
|
|
return pd.DataFrame([
|
|
# A state school and an independent: their mean, 37.65, is not DfE's figure.
|
|
dict(year=LATEST, local_authority="Kensington and Chelsea", local_authority_code=207, attainment_8_score=54.9),
|
|
dict(year=LATEST, local_authority="Kensington and Chelsea", local_authority_code=207, attainment_8_score=20.4),
|
|
dict(year=LATEST, local_authority="Bristol, City of", local_authority_code=801, attainment_8_score=45.0),
|
|
dict(year=LATEST, local_authority="West Sussex", local_authority_code=938, attainment_8_score=48.0),
|
|
# DfE publishes no LA figure for City of London.
|
|
dict(year=LATEST, local_authority="City of London", local_authority_code=201, attainment_8_score=30.0),
|
|
# A newer year with primary results only.
|
|
dict(year=202526, local_authority="Kensington and Chelsea", local_authority_code=207, attainment_8_score=np.nan),
|
|
])
|
|
|
|
|
|
class _Row:
|
|
def __init__(self, year, la_code, la_name, attainment_8_score):
|
|
self.year = year
|
|
self.la_code = la_code
|
|
self.la_name = la_name
|
|
self.attainment_8_score = attainment_8_score
|
|
|
|
|
|
class _StubSession:
|
|
rows = [
|
|
_Row(LATEST, 207, "Kensington and Chelsea", 54.5),
|
|
_Row(LATEST, 801, "Bristol City", 46.3), # DfE's spelling, not ours
|
|
_Row(LATEST, 938, "West Sussex", None), # suppressed
|
|
_Row(LATEST, 330, "Birmingham", 44.0), # no school of ours there
|
|
_Row(202324, 207, "Kensington and Chelsea", 54.5),
|
|
]
|
|
|
|
def query(self, model):
|
|
assert model.__name__ == "Ks4LaAverage"
|
|
return self
|
|
|
|
def filter(self, condition):
|
|
# The payload filters on year == <year>; apply it as Postgres would.
|
|
self._year = condition.right.value
|
|
return self
|
|
|
|
def all(self):
|
|
return [r for r in self.rows if r.year == self._year]
|
|
|
|
def rollback(self):
|
|
pass
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
|
|
class _OldYearOnly(_StubSession):
|
|
rows = [_Row(202324, 207, "Kensington and Chelsea", 54.5)]
|
|
|
|
|
|
class _NoMart(_StubSession):
|
|
def all(self):
|
|
raise RuntimeError('relation "marts.fact_ks4_la_averages" does not exist')
|
|
|
|
|
|
@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._la_averages_payload(_df())
|
|
|
|
return _run
|
|
|
|
|
|
def test_serves_dfe_figures_keyed_by_our_la_names(payload):
|
|
out = payload(_StubSession)
|
|
assert out["year"] == LATEST
|
|
assert out["secondary"]["attainment_8_by_la"] == {
|
|
"Kensington and Chelsea": 54.5,
|
|
"Bristol, City of": 46.3,
|
|
}
|
|
|
|
|
|
def test_an_la_without_a_dfe_figure_is_absent(payload):
|
|
by_la = payload(_StubSession)["secondary"]["attainment_8_by_la"]
|
|
assert "City of London" not in by_la
|
|
assert "West Sussex" not in by_la
|
|
assert None not in by_la.values()
|
|
|
|
|
|
def test_no_dfe_figures_for_the_year_give_an_empty_map(payload):
|
|
assert payload(_OldYearOnly) == {"year": LATEST, "secondary": {"attainment_8_by_la": {}}}
|
|
|
|
|
|
def test_a_missing_mart_gives_an_empty_map(payload):
|
|
assert payload(_NoMart)["secondary"]["attainment_8_by_la"] == {}
|
|
|
|
|
|
def test_the_endpoint_serves_dfe_figures(monkeypatch):
|
|
from backend import app as app_module
|
|
from backend import database as database_module
|
|
|
|
monkeypatch.setattr(app_module, "load_school_data", _df)
|
|
monkeypatch.setattr(database_module, "SessionLocal", _StubSession)
|
|
resp = TestClient(app_module.app).get("/api/la-averages")
|
|
assert resp.status_code == 200, resp.text
|
|
assert resp.json()["secondary"]["attainment_8_by_la"]["Kensington and Chelsea"] == 54.5
|