fix(search): no LA comparison for independent schools (H2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e659867590
commit
c7ddf0505d
6 files changed
+87
-6
No files matched your search
@@ -105,3 +105,20 @@ it('puts the card back when the pins are rebuilt for a reason other than the sch
|
||||
expect(container.querySelector('.sc-popup')).toHaveTextContent('Southmead Primary School');
|
||||
expect(container.querySelectorAll('.sc-pin--selected')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('compares a state secondary with its LA average on the card, but not an independent one', () => {
|
||||
const state: School = { ...base, urn: 3, school_name: 'Holland Park School', school_type: 'Academy converter',
|
||||
phase: 'Secondary', local_authority: 'Kensington and Chelsea', attainment_8_score: 60,
|
||||
latitude: 51.5, longitude: -0.2, distance: 0.3 };
|
||||
const independent: School = { ...state, urn: 4, school_name: 'Abbey Gate College',
|
||||
school_type: 'Other independent school', attainment_8_score: 20.4 };
|
||||
const laAverages = { 'Kensington and Chelsea': 54.5 };
|
||||
|
||||
const { container, rerender } = renderMap({ schools: [state, independent], laAverages, selectedUrn: 3 });
|
||||
expect(container.querySelector('.sc-popup')).toHaveTextContent('60.0 Att 8 +5.5 vs LA');
|
||||
|
||||
rerender({ schools: [state, independent], laAverages, selectedUrn: 4 });
|
||||
const card = container.querySelector('.sc-popup')!;
|
||||
expect(card).toHaveTextContent('20.4 Att 8');
|
||||
expect(card).not.toHaveTextContent(/vs LA/);
|
||||
});
|
||||
@@ -116,3 +116,38 @@ describe('SecondarySchoolRow shares the school page flags', () => {
|
||||
expect(screen.getByText('Fee-paying')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SecondarySchoolRow LA comparison', () => {
|
||||
it('compares a state school with its LA average', () => {
|
||||
render(
|
||||
<SecondarySchoolRow
|
||||
school={{ ...base, school_type: 'Academy converter', attainment_8_score: 60 }}
|
||||
laAvgAttainment8={54.5}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText(/\+5\.5 vs LA avg/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps the comparison for a school whose type is unknown', () => {
|
||||
render(
|
||||
<SecondarySchoolRow
|
||||
school={{ ...base, school_type: null, attainment_8_score: 60 } as unknown as School}
|
||||
laAvgAttainment8={54.5}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText(/\+5\.5 vs LA avg/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows an independent school's Attainment 8 without an LA comparison", () => {
|
||||
// DfE's LA average covers state-funded schools; an independent's
|
||||
// Attainment 8 leaves out IGCSEs (audit H2).
|
||||
render(
|
||||
<SecondarySchoolRow
|
||||
school={{ ...base, school_type: 'Other independent school', attainment_8_score: 20.4 }}
|
||||
laAvgAttainment8={54.5}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('20.4')).toBeInTheDocument();
|
||||
expect(screen.queryByText(/vs LA avg/)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -391,3 +391,20 @@ describe('singleSexLabel', () => {
|
||||
expect(singleSexLabel(undefined)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isIndependentSchool', () => {
|
||||
const { isIndependentSchool } = require('@/lib/utils');
|
||||
|
||||
it('matches both GIAS independent types', () => {
|
||||
expect(isIndependentSchool({ school_type: 'Other independent school' })).toBe(true);
|
||||
expect(isIndependentSchool({ school_type: 'Other independent special school' })).toBe(true);
|
||||
});
|
||||
|
||||
it('does not match state-funded types or a missing type', () => {
|
||||
for (const t of ['Academy converter', 'Community school', 'Free schools', 'Non-maintained special school']) {
|
||||
expect(isIndependentSchool({ school_type: t })).toBe(false);
|
||||
}
|
||||
expect(isIndependentSchool({ school_type: null })).toBe(false);
|
||||
expect(isIndependentSchool({})).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import type { School } from '@/lib/types';
|
||||
import { schoolUrl, isSpecialSchool, buildOfstedListBadge, listRwmValue } from '@/lib/utils';
|
||||
import { schoolUrl, isSpecialSchool, isIndependentSchool, buildOfstedListBadge, listRwmValue } from '@/lib/utils';
|
||||
|
||||
interface LeafletMapInnerProps {
|
||||
schools: School[];
|
||||
@@ -70,7 +70,7 @@ function metricHtml(school: School, { nationalAvgRwm, laAverages }: CardContext)
|
||||
const score = school.attainment_8_score;
|
||||
const laAvg = school.local_authority ? (laAverages?.[school.local_authority] ?? null) : null;
|
||||
let delta = '';
|
||||
if (!special && laAvg != null) {
|
||||
if (!special && !isIndependentSchool(school) && laAvg != null) {
|
||||
const diff = Math.round((score - laAvg) * 10) / 10;
|
||||
// Att8 runs 0–90 in 0.1 steps; ±0.5 is meaningful, where RWM needs ±2.
|
||||
const cls = diff >= 0.5 ? 'sc-up' : diff <= -0.5 ? 'sc-down' : '';
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
'use client';
|
||||
|
||||
import type { School } from '@/lib/types';
|
||||
import { buildOfstedListBadge, getPhaseStyle, schoolUrl, formatAgeRange, isProposedToClose, isSpecialSchool } from '@/lib/utils';
|
||||
import { buildOfstedListBadge, getPhaseStyle, schoolUrl, formatAgeRange, isProposedToClose, isSpecialSchool, isIndependentSchool } from '@/lib/utils';
|
||||
import { schoolFlags, schoolTypeLabel } from '@/lib/schoolFacts';
|
||||
import styles from './SecondarySchoolRow.module.css';
|
||||
|
||||
@@ -45,10 +45,12 @@ export function SecondarySchoolRow({
|
||||
const att8 = school.attainment_8_score;
|
||||
// The school's own Attainment 8 is a same-school figure — shown whenever it
|
||||
// exists (special schools included; their type tag on line 2 gives context).
|
||||
// Only the vs-LA-average delta, a benchmark comparison, is dropped for
|
||||
// special schools / PRUs / AP, whose pupils aren't measured against it fairly.
|
||||
// Only the vs-LA-average delta, a benchmark comparison, is dropped: for
|
||||
// special schools / PRUs / AP, whose pupils aren't measured against it
|
||||
// fairly, and for independent schools, because DfE's LA average covers
|
||||
// state-funded schools and an independent's Attainment 8 leaves out IGCSEs.
|
||||
const laDelta =
|
||||
att8 != null && !isSpecialSchool(school) && laAvgAttainment8 != null
|
||||
att8 != null && !isSpecialSchool(school) && !isIndependentSchool(school) && laAvgAttainment8 != null
|
||||
? att8 - laAvgAttainment8
|
||||
: null;
|
||||
|
||||
|
||||
@@ -758,6 +758,16 @@ export function isSpecialSchool(school: { school_type?: string | null }): boolea
|
||||
return /\bspecial\b/.test(t) || /pupil referral/.test(t) || /alternative provision/.test(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Independent (fee-paying) schools: GIAS types "Other independent school" and
|
||||
* "Other independent special school". DfE's Attainment 8 for them leaves out
|
||||
* IGCSEs, and DfE's LA averages cover state-funded schools only, so callers
|
||||
* drop the "vs LA avg" comparison for them (audit H2).
|
||||
*/
|
||||
export function isIndependentSchool(school: { school_type?: string | null }): boolean {
|
||||
return /\bindependent\b/i.test(school.school_type ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether GIAS records a religious character. "None", "Does not apply" and
|
||||
* "Not applicable" are the register's ways of saying it has none; the place
|
||||
|
||||
Reference in new issue
Block a user