11 KiB
Exam Results Taxonomy — Phase Grouping and Sixth-Form Separation
Date: 2026-07-07 Status: Approved design (taxonomy/analysis only — no implementation in this doc's scope)
Purpose
Classify every exam-result metric SchoolCompare displays today into four phase groups — Primary, Secondary, Sixth form, Other — and define an authoritative rule for separating schools that have a sixth form from those that don't. This document is the reference for:
- How the UI should group results sections and rankings by phase.
- The future KS5 (A-level) ingestion work — the Sixth form group lists the concrete DfE metrics as placeholders with source columns.
- Replacing the fragile
age_range contains "18"heuristic with the GIASOfficialSixthFormflag.
1. Grouping principle
Metrics are grouped by the key stage of the assessment, not by the phase of the school displaying them. An all-through school (4–18) shows metrics in all three exam groups; a pure primary shows only the Primary group.
| Group | Assessments | Key stage | Taken at age | Data status |
|---|---|---|---|---|
| Primary | KS2 SATs (reading, writing TA, maths, GPS, science TA) | KS2 | 10–11 (Year 6) | ✅ Live — marts.fact_ks2_performance |
| Secondary | GCSEs, Attainment 8 / Progress 8, EBacc | KS4 | 15–16 (Year 11) | ✅ Live — marts.fact_ks4_performance |
| Sixth form | A levels, applied general, tech levels | KS5 (16–18) | 17–18 (Year 12–13) | ⏳ Not ingested — placeholders in §4 |
| Other | Non-exam context displayed alongside results | n/a | n/a | ✅ Live — various marts |
Not covered (not displayed today, candidates for future "Other"/Primary): EYFS Good Level of Development, Year 1 Phonics check, Year 4 Multiplication Tables Check, KS1 assessments (no longer published at school level by DfE).
2. Metric-by-metric mapping (current site)
Every key in backend/schemas.py METRIC_DEFINITIONS — the single source of
truth for what the site displays — mapped to its phase group. category is
the existing schema category; source columns are the DfE names used at
ingestion (legacy performance-tables CSV for KS2, EES for KS4).
Primary (KS2 SATs)
| Metric key | Category | DfE source column |
|---|---|---|
rwm_expected_pct |
expected | PTRWM_EXP |
reading_expected_pct |
expected | PTREAD_EXP |
writing_expected_pct |
expected | PTWRITTA_EXP |
maths_expected_pct |
expected | PTMAT_EXP |
gps_expected_pct |
expected | PTGPS_EXP |
science_expected_pct |
expected | PTSCITA_EXP |
rwm_high_pct |
higher | PTRWM_HIGH |
reading_high_pct |
higher | PTREAD_HIGH |
writing_high_pct |
higher | PTWRITTA_HIGH |
maths_high_pct |
higher | PTMAT_HIGH |
gps_high_pct |
higher | PTGPS_HIGH |
reading_progress |
progress | READPROG |
writing_progress |
progress | WRITPROG |
maths_progress |
progress | MATPROG |
reading_avg_score |
average | READ_AVERAGE |
maths_avg_score |
average | MAT_AVERAGE |
gps_avg_score |
average | GPS_AVERAGE |
rwm_expected_boys_pct |
gender | PTRWM_EXP_B |
rwm_expected_girls_pct |
gender | PTRWM_EXP_G |
rwm_high_boys_pct |
gender | PTRWM_HIGH_B |
rwm_high_girls_pct |
gender | PTRWM_HIGH_G |
rwm_expected_disadvantaged_pct |
equity | PTRWM_EXP_FSM6CLA1A |
rwm_expected_non_disadvantaged_pct |
equity | PTRWM_EXP_NotFSM6CLA1A |
disadvantaged_gap |
equity | DIFFN_RWM_EXP |
reading_absence_pct |
absence | PTREAD_AT |
gps_absence_pct |
absence | PTGPS_AT |
maths_absence_pct |
absence | PTMAT_AT |
writing_absence_pct |
absence | PTWRITTA_AD |
science_absence_pct |
absence | PTSCITA_AD |
rwm_expected_3yr_pct |
trends | PTRWM_EXP_3YR |
reading_avg_3yr |
trends | READ_AVERAGE_3YR |
maths_avg_3yr |
trends | MAT_AVERAGE_3YR |
The absence metrics measure absence from KS2 tests, so they belong to
Primary even though they are not attainment scores. National comparators for
this group come from marts.fact_ks2_national_averages.
Secondary (KS4 / GCSE)
| Metric key | Category | EES source column |
|---|---|---|
attainment_8_score |
gcse | attainment8_average |
progress_8_score |
gcse | progress8_average |
english_maths_standard_pass_pct |
gcse | engmath_94_percent |
english_maths_strong_pass_pct |
gcse | engmath_95_percent |
ebacc_entry_pct |
gcse | ebacc_entering_percent |
ebacc_standard_pass_pct |
gcse | ebacc_94_percent |
ebacc_strong_pass_pct |
gcse | ebacc_95_percent |
ebacc_avg_score |
gcse | ebacc_aps_average |
gcse_grade_91_pct |
gcse | gcse_91_percent |
Also stored in marts.fact_ks4_performance (and fact_performance) but not
yet in METRIC_DEFINITIONS — Secondary group members when surfaced:
progress_8_lower_ci, progress_8_upper_ci, progress_8_english,
progress_8_maths, progress_8_ebacc, progress_8_open,
prior_attainment_avg (KS2 baseline of the GCSE cohort), sen_pct.
Sixth form (KS5)
No metrics today. The secondary school detail view renders a static note ("Post-16 destination data coming soon") when the school has a sixth form. Placeholders for ingestion are specified in §4.
Other (non-exam context)
Displayed alongside results but not tied to any assessment:
| Metric key / surface | Category | Source |
|---|---|---|
disadvantaged_pct |
context | KS2 CSV PTFSM6CLA1A |
eal_pct |
context | KS2 CSV PTEALGRP2 |
sen_support_pct |
context | KS2 CSV PSENELK (KS4 fallback sen_no_ehcp_pupil_percent) |
stability_pct |
context | KS2 CSV PTMOBN |
Ofsted grades incl. sixth_form_provision / rc_sixth_form |
— | marts.fact_ofsted_inspection |
| Admissions (offers, oversubscription) | — | marts.fact_admissions |
| Finance (per-pupil spend, cost shares) | — | marts.fact_finance |
| Deprivation (IDACI) | — | marts.fact_deprivation |
| Pupil characteristics (census) | — | marts.fact_pupil_characteristics |
Note: the context metrics are cohort characteristics of the KS2 cohort at source, but they are presented (and should stay presented) as school-level context, so they group as Other, not Primary.
3. Sixth-form separation
Definition (authoritative)
A school has a sixth form iff GIAS
OfficialSixthForm (name)="Has a sixth form"for its URN.
GIAS values are Has a sixth form, Does not have a sixth form, and
Not applicable / blank. Not applicable (nurseries, primaries, PRUs) maps
to false. This field is the DfE's registry flag, updated continuously,
and is the only source that correctly classifies:
- 16–19 sixth-form colleges and UTCs (age ranges like
14-19,16-19that the current substring heuristic misclassifies as no sixth form); - schools whose statutory age range extends to 18 on paper but which have no registered post-16 provision.
Pipeline change (implemented 2026-07-07)
stg_gias_establishments.sql: add"OfficialSixthForm (name)" as official_sixth_form.dim_school.sql(+models.pyDimSchool,_marts_schema.yml): addhas_sixth_form boolean=official_sixth_form = 'Has a sixth form'.- Expose
has_sixth_formon the school API payloads.
Implemented in feat/gias-sixth-form-flag — see
docs/superpowers/plans/2026-07-07-gias-sixth-form-flag.md.
Current heuristic — audit of age_range ~ "18" sites
All must migrate to the has_sixth_form flag once exposed:
| Site | Current behaviour |
|---|---|
backend/app.py:419-422 |
/api/schools?has_sixth_form=yes|no filters on age_range.str.contains("18") |
nextjs-app/components/SecondarySchoolDetailView.tsx:101 |
"Sixth form" badge + coming-soon note from age_range?.includes('18') |
nextjs-app/components/FilterBar.tsx:370-372 |
Filter labels hard-code "(11-18)" / "(11-16)" — labels should drop the age-range parenthetical since sixth form ≠ age range |
Fallback rule: if GIAS is blank for a URN (rare; new establishments), fall back to the age-range heuristic and log the URN.
UI separation rules
- School page: schools with
has_sixth_form = trueshow a Sixth form results section (placeholder until KS5 data lands); schools without never show it. Badge on the header as today, but driven by the flag. - Search/rankings filter: "With sixth form" / "Without sixth form" uses the flag; applies to secondary and all-through phases.
- Comparison: when comparing a with-sixth-form school against one without, the Sixth form group renders "No sixth form" for the latter rather than blank cells, making the structural difference explicit.
4. Sixth form placeholders — future KS5 ingestion spec
Source: DfE "A level and other 16 to 18 results" (EES, preferred — matches
the KS4 EES tap) or legacy performance-tables england_ks5final.csv.
Column names below are from the legacy KS5 CSV; verify against the EES
release chosen at ingestion time.
| Proposed metric key | Name | Legacy source column | Type |
|---|---|---|---|
alevel_aps_per_entry |
A level average points per entry | TALLPPE_ALEV_1618 |
score |
alevel_avg_grade |
A level average grade (e.g. B-) | TALLPPEGRD_ALEV_1618 |
grade |
academic_aps_per_entry |
Academic qualifications APS per entry | TALLPPE_ACAD_1618 |
score |
applied_general_aps_per_entry |
Applied general APS per entry | TALLPPE_AGEN_1618 |
score |
tech_level_aps_per_entry |
Tech level APS per entry | TALLPPE_TLEV_1618 |
score |
english_progress_1618 |
English progress (16–18, unfinished GCSE 4+) | PROGENG_1618 |
score |
maths_progress_1618 |
Maths progress (16–18) | PROGMAT_1618 |
score |
ks5_cohort_size |
Students at end of 16–18 study | TALLPUP_1618 |
count |
alevel_3plus_aab_pct |
% achieving AAB+ in ≥2 facilitating subjects | TAAB2FAC_1618 |
percentage |
ks5_retention_pct |
Retention (completed main programme) | study-programme retention measure | percentage |
ks5_destinations_pct |
Sustained education/employment destination | 16–18 destination measures dataset | percentage |
Proposed landing shape mirrors KS4: stg_ees_ks5.sql →
int_ks5_with_lineage.sql → marts.fact_ks5_performance (one row per URN
per year), joined into fact_performance, with a category: "sixth_form"
(or "alevel") block added to METRIC_DEFINITIONS.
5. Out of scope
- Any implementation (pipeline, API, or UI changes) — this is the taxonomy reference; implementation work items are §3 "Pipeline change", the heuristic migration audit, and §4 ingestion, each to be planned separately.
- Middle schools (deemed secondary/primary): they follow the assessment-based grouping automatically — no special casing.
- Independent schools: no DfE performance data published; unaffected.