fix(api): treat "16 plus" as secondary, the way PHASE_GROUPS already does
PR Checks / Frontend Typecheck + Tests (pull_request) Canceled after 9s
PR Checks / Backend Smoke (pull_request) Canceled after 0s
PR Checks / Build Backend (no push) (pull_request) Canceled after 0s
PR Checks / Build Frontend (no push) (pull_request) Canceled after 0s
PR Checks / Build Pipeline (no push) (pull_request) Canceled after 0s
PR Checks / AI Code Review (Claude) (pull_request) Canceled after 0s
PR Checks / Frontend Typecheck + Tests (pull_request) Canceled after 9s
PR Checks / Backend Smoke (pull_request) Canceled after 0s
PR Checks / Build Backend (no push) (pull_request) Canceled after 0s
PR Checks / Build Frontend (no push) (pull_request) Canceled after 0s
PR Checks / Build Pipeline (no push) (pull_request) Canceled after 0s
PR Checks / AI Code Review (Claude) (pull_request) Canceled after 0s
GIAS phase 6 is "16 plus", and PHASE_GROUPS deliberately files it in the secondary group. The payload helper decided the same question with `"secondary" in phase_text`, which that value does not satisfy — so a sixth-form college was handed the primary bucket and offered infant schools as its peers, with the KS2 metric key to label them. No crash; just a page confidently showing the wrong schools. The decision now lives in similar_schools.is_secondary_phase, beside the PHASE_GROUPS bucket it selects from, so the two cannot drift again. A test pins them together. The same binary assumption had a second output. computeSchoolFlags tests for the substring too, so a 16-plus school renders the primary template, and the composer was labelling the section from the template: "Other primary schools near <sixth form college>" above a row of secondaries. The section now derives its noun from the school's own phase, which also removes the duplicated wording from both composers. A 16-plus school's candidates span the whole secondary group, so no single noun fits and it gets the honest general one. Reported in review on #150. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bd2a6c385b
commit
2175dccb7c
7 files changed
+121
-14
No files matched your search
@@ -155,7 +155,7 @@ export function PrimarySchoolSections({
|
||||
<SimilarSchoolsSection
|
||||
urn={schoolInfo.urn}
|
||||
schoolName={schoolInfo.school_name}
|
||||
phaseNoun={flags.isAllThrough ? 'all-through' : 'primary'}
|
||||
phase={schoolInfo.phase}
|
||||
thisMetricValue={flags.latestResults?.rwm_expected_pct ?? null}
|
||||
similar={similarSchools}
|
||||
/>
|
||||
|
||||
@@ -149,7 +149,7 @@ export function SecondarySchoolSections({
|
||||
<SimilarSchoolsSection
|
||||
urn={schoolInfo.urn}
|
||||
schoolName={schoolInfo.school_name}
|
||||
phaseNoun="secondary"
|
||||
phase={schoolInfo.phase}
|
||||
thisMetricValue={flags.latestResults?.attainment_8_score ?? null}
|
||||
similar={similarSchools}
|
||||
/>
|
||||
|
||||
@@ -28,6 +28,28 @@ export function shouldRenderSimilar(similar?: SimilarSchool[] | null): boolean {
|
||||
return (similar?.length ?? 0) >= MINIMUM;
|
||||
}
|
||||
|
||||
/**
|
||||
* What the lede calls the set of schools it is showing.
|
||||
*
|
||||
* Derived from the school's own GIAS phase rather than the template it renders
|
||||
* with, because those disagree for "16 plus" (GIAS phase 6): a sixth-form
|
||||
* college renders the primary template — computeSchoolFlags tests for the
|
||||
* substring "secondary" — while the backend correctly matches it against the
|
||||
* secondary group. Taking the noun from the template would print "Other primary
|
||||
* schools near <sixth form college>" above a row of secondaries.
|
||||
*
|
||||
* A 16-plus school's candidates span the whole secondary group, so no single
|
||||
* noun fits and it gets the honest general one.
|
||||
*/
|
||||
export function nearbyNoun(phase: string | null | undefined): string {
|
||||
const text = (phase ?? '').trim().toLowerCase();
|
||||
if (text === 'all-through') return 'all-through schools';
|
||||
if (text === '16 plus') return 'schools and colleges';
|
||||
if (text.includes('secondary')) return 'secondary schools';
|
||||
if (text.includes('primary')) return 'primary schools';
|
||||
return 'schools';
|
||||
}
|
||||
|
||||
function metricLabel(key: string): string {
|
||||
return key === 'attainment_8_score' ? 'Attainment 8' : 'Reading, writing & maths';
|
||||
}
|
||||
@@ -40,13 +62,14 @@ function formatMetric(value: number | null, key: string): string {
|
||||
export function SimilarSchoolsSection({
|
||||
urn,
|
||||
schoolName,
|
||||
phaseNoun,
|
||||
phase,
|
||||
thisMetricValue,
|
||||
similar,
|
||||
}: {
|
||||
urn: number;
|
||||
schoolName: string;
|
||||
phaseNoun: string;
|
||||
/** The school's own GIAS phase, not the template it renders with. */
|
||||
phase: string | null | undefined;
|
||||
thisMetricValue: number | null;
|
||||
similar?: SimilarSchool[] | null;
|
||||
}) {
|
||||
@@ -57,6 +80,7 @@ export function SimilarSchoolsSection({
|
||||
// shares an intake with this school.
|
||||
const loosest = Math.max(...schools.map((s) => s.tier));
|
||||
const metricKey = schools[0].metric_key;
|
||||
const noun = nearbyNoun(phase);
|
||||
|
||||
return (
|
||||
<Section id="similar">
|
||||
@@ -70,8 +94,8 @@ export function SimilarSchoolsSection({
|
||||
</h2>
|
||||
<p className={styles.lede}>
|
||||
{loosest >= 3
|
||||
? `Other ${phaseNoun} schools near ${schoolName}.`
|
||||
: `Other ${phaseNoun} schools near ${schoolName}, with a similar intake.`}
|
||||
? `Other ${noun} near ${schoolName}.`
|
||||
: `Other ${noun} near ${schoolName}, with a similar intake.`}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user