fix(compare): per-school P8 explanation; disadvantaged cohort from the same yearly row
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m3s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 10s
PR Checks / Build Frontend (no push) (pull_request) Successful in 47s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 2m24s
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m3s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 10s
PR Checks / Build Frontend (no push) (pull_request) Successful in 47s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 2m24s
Review findings: (1) the 'no KS2 baseline (COVID)' explanation was derived from the basket-wide max year, so a school with an unrelated data gap borrowed it from a neighbour with 2024/25 data — now judged per school on its own latest year. (2) The '~N disadvantaged pupils' cohort multiplied eligible_pupils and disadvantaged_pct resolved independently across years — now both come from the same yearly row that supplies the displayed percentage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
@@ -150,13 +150,15 @@ export function CompareAcademics({
|
|||||||
});
|
});
|
||||||
// DfE stopped publishing Progress 8 from 2024/25: those GCSE year groups
|
// DfE stopped publishing Progress 8 from 2024/25: those GCSE year groups
|
||||||
// sat no KS2 tests (COVID), so there is no baseline to measure progress
|
// sat no KS2 tests (COVID), so there is no baseline to measure progress
|
||||||
// from. A bare "No data" reads as a gap on our side — say why.
|
// from. A bare "No data" reads as a gap on our side — say why. Judged
|
||||||
const latestYear = urns.reduce((max, urn) => {
|
// PER SCHOOL on its own latest data year: a school whose data simply
|
||||||
|
// stops earlier (an unrelated gap) must not borrow the COVID explanation
|
||||||
|
// from a neighbour that does have 2024/25 data.
|
||||||
|
const p8NotPublished = urns.map((urn) => {
|
||||||
const rows = data[String(urn)]?.yearly_data ?? [];
|
const rows = data[String(urn)]?.yearly_data ?? [];
|
||||||
const y = rows.length ? Math.trunc(rows[rows.length - 1].year) : 0;
|
const y = rows.length ? Math.trunc(rows[rows.length - 1].year) : 0;
|
||||||
return Math.max(max, y);
|
return y >= 202425;
|
||||||
}, 0);
|
});
|
||||||
const p8NotPublished = latestYear >= 202425;
|
|
||||||
const grade5 = latestValues(data, urns, 'english_maths_strong_pass_pct');
|
const grade5 = latestValues(data, urns, 'english_maths_strong_pass_pct');
|
||||||
const ebacc = latestValues(data, urns, 'ebacc_entry_pct');
|
const ebacc = latestValues(data, urns, 'ebacc_entry_pct');
|
||||||
const att8Anchor = nationalAverages?.secondary?.attainment_8_score;
|
const att8Anchor = nationalAverages?.secondary?.attainment_8_score;
|
||||||
@@ -198,7 +200,7 @@ export function CompareAcademics({
|
|||||||
>
|
>
|
||||||
{banding[i]}
|
{banding[i]}
|
||||||
</Chip>
|
</Chip>
|
||||||
) : p8NotPublished ? (
|
) : p8NotPublished[i] ? (
|
||||||
<span className={s.small}>
|
<span className={s.small}>
|
||||||
Not published — this GCSE year group sat no KS2 tests (COVID), so DfE has no
|
Not published — this GCSE year group sat no KS2 tests (COVID), so DfE has no
|
||||||
baseline to measure progress from
|
baseline to measure progress from
|
||||||
@@ -234,15 +236,21 @@ export function CompareAcademics({
|
|||||||
const disadvantagedAnchor = benchmarks?.primary?.disadvantaged_rwm_expected_pct ?? null;
|
const disadvantagedAnchor = benchmarks?.primary?.disadvantaged_rwm_expected_pct ?? null;
|
||||||
// Cohort size behind the disadvantaged figure (spec §8.5): these are small
|
// Cohort size behind the disadvantaged figure (spec §8.5): these are small
|
||||||
// groups where single pupils move the percentage — show roughly how many
|
// groups where single pupils move the percentage — show roughly how many
|
||||||
// pupils the figure rests on.
|
// pupils the figure rests on. Taken from the SAME yearly row that supplies
|
||||||
const eligible = latestValues(data, urns, 'eligible_pupils');
|
// the displayed percentage: resolving eligible_pupils and the
|
||||||
const disadvantagedShare = latestValues(data, urns, 'disadvantaged_pct');
|
// disadvantaged share independently could mix years and misstate the
|
||||||
const cohorts = urns.map((_, i) => {
|
// cohort behind the figure.
|
||||||
const n = eligible[i];
|
const cohorts = urns.map((urn) => {
|
||||||
const share = disadvantagedShare[i];
|
const rows = data[String(urn)]?.yearly_data ?? [];
|
||||||
if (n == null || share == null) return null;
|
for (let i = rows.length - 1; i >= 0; i--) {
|
||||||
const cohort = Math.round((n * share) / 100);
|
const row = rows[i];
|
||||||
return cohort > 0 ? cohort : null;
|
if (row.rwm_expected_disadvantaged_pct != null) {
|
||||||
|
if (row.eligible_pupils == null || row.disadvantaged_pct == null) return null;
|
||||||
|
const cohort = Math.round((row.eligible_pupils * row.disadvantaged_pct) / 100);
|
||||||
|
return cohort > 0 ? cohort : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user