feat: add secondary school support with KS4 data and metric tooltips
Some checks failed
Build and Push Docker Images / Build Frontend (Next.js) (push) Has been cancelled
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Has been cancelled
Build and Push Docker Images / Trigger Portainer Update (push) Has been cancelled
Build and Push Docker Images / Build Backend (FastAPI) (push) Has been cancelled

- Backend: replace INNER JOIN ks2 with UNION ALL (ks2 + ks4) so primary
  and secondary schools both appear in the main DataFrame
- Backend: add /api/national-averages endpoint computing means from live
  data, replacing the hardcoded NATIONAL_AVG constant on the frontend
- Backend: add phase filter param to /api/schools; return phases from
  /api/filters; fix hardcoded "phase": "Primary" in school detail endpoint
- Backend: add KS4 metric definitions (Attainment 8, Progress 8, EBacc,
  English & Maths pass rates) to METRIC_DEFINITIONS and RANKING_COLUMNS
- Frontend: SchoolDetailView is now phase-aware — secondary schools show
  a GCSE Results section (Att8, P8, E&M, EBacc) instead of SATs; phonics
  tab hidden for secondary; admissions says Year 7 instead of Year 3;
  history table shows KS4 columns; chart datasets switch for secondary
- Frontend: new MetricTooltip component (CSS-only ⓘ icon) backed by
  METRIC_EXPLANATIONS — added to RWM, GPS, SEN, EAL, IDACI, progress
  scores and all KS4 metrics throughout SchoolDetailView and SchoolCard
- Frontend: METRIC_EXPLANATIONS extended with KS4 terms (Attainment 8,
  Progress 8, EBacc) and previously missing terms (SEN, EHCP, EAL, IDACI)
- Frontend: SchoolCard expands "RWM" to "Reading, Writing & Maths" and
  shows Attainment 8 / English & Maths Grade 4+ for secondary schools
- Frontend: FilterBar adds Phase dropdown (Primary / Secondary / All-through)
- Frontend: HomeView hero copy updated; compact list shows phase-aware metric
- Global metadata updated to remove "primary only" framing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-28 14:59:40 +00:00
parent b0990e30ee
commit 5eff9af69c
16 changed files with 903 additions and 187 deletions

View File

@@ -98,6 +98,66 @@ export const METRIC_EXPLANATIONS: Record<string, MetricExplanation> = {
plain: 'Difference in attainment between disadvantaged pupils and their peers',
detail: 'A smaller gap means the school is doing more to support disadvantaged pupils.',
},
sen_support_pct: {
label: 'SEN Support',
plain: '% of pupils receiving SEN (Special Educational Needs) support without a formal plan',
detail: 'These pupils need extra help but do not yet have an Education, Health and Care Plan (EHCP).',
},
sen_ehcp_pct: {
label: 'Education, Health and Care Plan (EHCP)',
plain: '% of pupils with a formal EHCP — a legal plan for pupils with significant additional needs',
},
eal_pct: {
label: 'English as an Additional Language',
plain: '% of pupils whose first language is not English',
},
idaci_decile: {
label: 'Deprivation (IDACI)',
plain: 'How deprived the area around the school is. Decile 1 = most deprived 10% of areas in England.',
detail: 'IDACI stands for Income Deprivation Affecting Children Index. It measures the proportion of children living in low-income households in an area.',
},
// ── KS4 / GCSE metrics ────────────────────────────────────────────────
attainment_8_score: {
label: 'Attainment 8',
plain: 'Average grade across a pupil\'s best 8 GCSEs, including English and Maths',
detail: 'Each GCSE grade is converted to a points score (grade 9 = 9 points, grade 1 = 1 point). The national average is around 46.',
},
progress_8_score: {
label: 'Progress 8',
plain: 'How much pupils improved from their primary school results to GCSE, compared to similar pupils nationally',
detail: '0 = national average. Positive means better-than-expected progress; negative means lower-than-expected. A score above +0.5 is considered well above average.',
},
english_maths_standard_pass_pct: {
label: 'English & Maths — Grade 4+ (Standard Pass)',
plain: '% of pupils achieving at least a grade 4 in both English and Maths',
detail: 'Grade 4 is the minimum "standard pass". Employers and colleges often require grade 4 in English and Maths.',
},
english_maths_strong_pass_pct: {
label: 'English & Maths — Grade 5+ (Strong Pass)',
plain: '% of pupils achieving at least a grade 5 in both English and Maths',
detail: 'Grade 5 is a "strong pass". Many sixth forms and universities expect grade 5 in English and Maths.',
},
ebacc_entry_pct: {
label: 'EBacc Entry',
plain: '% of pupils who entered the English Baccalaureate — a set of GCSE subjects covering English, Maths, Sciences, a Language, and Humanities',
detail: 'EBacc entry keeps academic options open post-16. It is not a separate qualification.',
},
ebacc_standard_pass_pct: {
label: 'EBacc — Grade 4+ (Standard Pass)',
plain: '% of pupils achieving grade 4 or above across all EBacc subjects',
},
ebacc_strong_pass_pct: {
label: 'EBacc — Grade 5+ (Strong Pass)',
plain: '% of pupils achieving grade 5 or above across all EBacc subjects',
},
ebacc_avg_score: {
label: 'EBacc Average Score',
plain: 'Average points score across all EBacc subject entries',
},
gcse_grade_91_pct: {
label: 'GCSE Grade 91 %',
plain: '% of GCSE entries where a pupil achieved a grade between 9 (highest) and 1',
},
};
/**

View File

@@ -48,6 +48,13 @@ export interface School {
// Location search fields
distance?: number | null;
// Phase (Primary, Secondary, All-through, etc.)
phase?: string | null;
// KS4 card metrics
attainment_8_score?: number | null;
english_maths_standard_pass_pct?: number | null;
// GIAS enrichment fields
website?: string | null;
headteacher_name?: string | null;
@@ -225,6 +232,24 @@ export interface SchoolResult {
rwm_expected_3yr_pct: number | null;
reading_avg_3yr: number | null;
maths_avg_3yr: number | null;
// KS4 / GCSE metrics
attainment_8_score: number | null;
progress_8_score: number | null;
progress_8_lower_ci: number | null;
progress_8_upper_ci: number | null;
progress_8_english: number | null;
progress_8_maths: number | null;
progress_8_ebacc: number | null;
progress_8_open: number | null;
english_maths_strong_pass_pct: number | null;
english_maths_standard_pass_pct: number | null;
ebacc_entry_pct: number | null;
ebacc_strong_pass_pct: number | null;
ebacc_standard_pass_pct: number | null;
ebacc_avg_score: number | null;
gcse_grade_91_pct: number | null;
prior_attainment_avg: number | null;
}
// ============================================================================
@@ -304,6 +329,13 @@ export interface Filters {
local_authorities: string[];
school_types: string[];
years: number[];
phases: string[];
}
export interface NationalAverages {
year: number;
primary: Record<string, number>;
secondary: Record<string, number>;
}
// Backend returns filters directly, not wrapped
@@ -338,6 +370,7 @@ export interface SchoolSearchParams {
search?: string;
local_authority?: string;
school_type?: string;
phase?: string;
postcode?: string;
radius?: number;
page?: number;