fix(compare): census-sourced FSM/EAL benchmarks; never fall back across measure definitions

The FSM chip anchored against disadvantaged_pct (a different measure,
FSM6+CLA) whenever fsm_pct was null — which it always was, since the
performance df has no fsm_pct. New fact_census_benchmarks mart supplies
pupil-weighted FSM/EAL means per phase; the KS2-column medians that
produced a bogus 50% 'secondary disadvantaged' anchor are gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
Tudor
2026-07-16 19:05:15 +01:00
co-authored by Claude Fable 5
parent 9773483221
commit 1d855f3c17
7 changed files with 142 additions and 24 deletions
+17 -14
View File
@@ -525,13 +525,20 @@ def get_data_info(db: Session = None) -> dict:
# SUPPLEMENTARY DATA — per-school detail page
# =============================================================================
def compute_benchmarks(df: pd.DataFrame) -> dict:
def compute_benchmarks(df: pd.DataFrame, census_benchmarks: dict | None = None) -> dict:
"""State-school benchmarks computed from our dataset (spec §5/§8.6).
NOT official DfE figures — consumers must label them
"state-school average (computed from our dataset)". The disadvantaged
attainment average is weighted by cohort size (eligible_pupils) so
small schools don't dominate; context measures are medians.
small schools don't dominate.
Context measures (FSM/EAL/pupil counts) come from `census_benchmarks`
(the fact_census_benchmarks mart, pupil-weighted, keyed by phase): the
performance df has no fsm_pct at all, and its eal/disadvantaged columns
are KS2-only — medianing them for "secondary" produced junk anchors
from the handful of all-through schools. When the mart is unavailable
these are None; never fall back across measure definitions.
"""
if df.empty or "year" not in df.columns:
return {}
@@ -567,18 +574,14 @@ def compute_benchmarks(df: pd.DataFrame) -> dict:
)
return round(float(w), 1)
def _block(sub, with_disadvantaged):
median_pupils = None
if "total_pupils" in sub.columns:
mp = sub["total_pupils"].median()
if pd.notna(mp):
median_pupils = int(mp)
def _block(sub, phase, with_disadvantaged):
census = (census_benchmarks or {}).get(phase) or {}
block = {
"eal_pct": _median(sub, "eal_pct"),
"eal_pct": census.get("eal_pct"),
"sen_support_pct": _median(sub, "sen_support_pct"),
"disadvantaged_pct": _median(sub, "disadvantaged_pct"),
"fsm_pct": _median(sub, "fsm_pct"),
"median_pupils": median_pupils,
"disadvantaged_pct": _median(sub, "disadvantaged_pct") if with_disadvantaged else None,
"fsm_pct": census.get("fsm_pct"),
"median_pupils": census.get("median_pupils"),
}
if with_disadvantaged:
block["disadvantaged_rwm_expected_pct"] = _weighted_disadvantaged(sub)
@@ -587,8 +590,8 @@ def compute_benchmarks(df: pd.DataFrame) -> dict:
return {
"source": "state-school average (computed from our dataset)",
"year": int(latest_year),
"primary": _block(prim, with_disadvantaged=True),
"secondary": _block(sec, with_disadvantaged=False),
"primary": _block(prim, "primary", with_disadvantaged=True),
"secondary": _block(sec, "secondary", with_disadvantaged=False),
}