Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e36125b24a |
@@ -9,7 +9,6 @@ import {
|
||||
isValidPostcode,
|
||||
debounce,
|
||||
buildOfstedListBadge,
|
||||
ofstedLegacyAreas,
|
||||
metricKind,
|
||||
shortName,
|
||||
computeYBounds,
|
||||
@@ -112,35 +111,6 @@ describe('debounce', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('ofstedLegacyAreas', () => {
|
||||
it('returns only real grades (1-4) and drops sentinel/null values', () => {
|
||||
const areas = ofstedLegacyAreas({
|
||||
quality_of_education: 1,
|
||||
behaviour_attitudes: 2,
|
||||
personal_development: null,
|
||||
leadership_management: 9, // sentinel "not applicable" — must be dropped
|
||||
early_years_provision: 9, // secondary school — not applicable
|
||||
sixth_form_provision: 2,
|
||||
});
|
||||
expect(areas.map((a) => a.label)).toEqual([
|
||||
'Quality of Teaching',
|
||||
'Behaviour in School',
|
||||
'Sixth Form',
|
||||
]);
|
||||
expect(areas.every((a) => a.value >= 1 && a.value <= 4)).toBe(true);
|
||||
});
|
||||
|
||||
it('includes the sixth-form judgement where present (was previously dropped)', () => {
|
||||
const areas = ofstedLegacyAreas({ sixth_form_provision: 1 });
|
||||
expect(areas).toEqual([{ label: 'Sixth Form', value: 1 }]);
|
||||
});
|
||||
|
||||
it('includes Early Years for a primary school', () => {
|
||||
const areas = ofstedLegacyAreas({ quality_of_education: 2, early_years_provision: 1 });
|
||||
expect(areas.map((a) => a.label)).toContain('Early Years (Reception)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildOfstedListBadge', () => {
|
||||
it('returns grade word + year for OEIF Outstanding', () => {
|
||||
const badge = buildOfstedListBadge({ ofsted_grade: 1, ofsted_date: '2023-11-15', ofsted_framework: 'OEIF' });
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {
|
||||
SchoolDeprivation, SchoolFinance, NationalAverages,
|
||||
} from '@/lib/types';
|
||||
import {
|
||||
formatPercentage, formatProgress, formatAcademicYear, isProposedToClose, ofstedLegacyAreas,
|
||||
formatPercentage, formatProgress, formatAcademicYear, isProposedToClose,
|
||||
} from '@/lib/utils';
|
||||
import { DeltaChip } from './DeltaChip';
|
||||
|
||||
@@ -285,12 +285,17 @@ export function SchoolDetailView({
|
||||
: ofsted?.inspection_date ?? null;
|
||||
|
||||
// ── Ofsted: detect if all OEIF sub-grades match the overall ───────────
|
||||
const oeifAreas = ofsted ? ofstedLegacyAreas(ofsted) : [];
|
||||
const oeifAllSameGrade =
|
||||
!!ofsted &&
|
||||
!isReportCard &&
|
||||
oeifAreas.length >= 3 &&
|
||||
oeifAreas.every((a) => a.value === ofsted.overall_effectiveness);
|
||||
const oeifAllSameGrade = (() => {
|
||||
if (!ofsted || isReportCard) return false;
|
||||
const subs = [
|
||||
ofsted.quality_of_education,
|
||||
ofsted.behaviour_attitudes,
|
||||
ofsted.personal_development,
|
||||
ofsted.leadership_management,
|
||||
...(ofsted.early_years_provision != null ? [ofsted.early_years_provision] : []),
|
||||
].filter((v): v is number => v != null);
|
||||
return subs.length >= 3 && subs.every(v => v === ofsted.overall_effectiveness);
|
||||
})();
|
||||
|
||||
// Label shown in the mobile "section" menu button — the section in view.
|
||||
const activeNavLabel = (navItems.find((n) => n.id === activeSection) ?? navItems[0])?.label ?? '';
|
||||
@@ -578,9 +583,7 @@ export function SchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
<p className={styles.ofstedDisclaimer}>
|
||||
{ofsted.grade_source === 'ungraded_carried_forward'
|
||||
? 'This overall grade is carried forward from an earlier inspection — Ofsted has since visited without issuing a new overall grade. From September 2024, Ofsted no longer makes an overall effectiveness judgement.'
|
||||
: 'From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections of state-funded schools.'}
|
||||
From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections of state-funded schools.
|
||||
</p>
|
||||
{oeifAllSameGrade ? (
|
||||
<p className={styles.ofstedAllSame}>
|
||||
@@ -588,7 +591,15 @@ export function SchoolDetailView({
|
||||
</p>
|
||||
) : (
|
||||
<div className={styles.metricsGrid}>
|
||||
{oeifAreas.map(({ label, value }) => (
|
||||
{[
|
||||
{ label: 'Quality of Teaching', value: ofsted.quality_of_education },
|
||||
{ label: 'Behaviour in School', value: ofsted.behaviour_attitudes },
|
||||
{ label: 'Pupils\' Wider Development', value: ofsted.personal_development },
|
||||
{ label: 'School Leadership', value: ofsted.leadership_management },
|
||||
...(ofsted.early_years_provision != null
|
||||
? [{ label: 'Early Years (Reception)', value: ofsted.early_years_provision }]
|
||||
: []),
|
||||
].map(({ label, value }) => value != null && (
|
||||
<div key={label} className={styles.metricCard}>
|
||||
<div className={styles.metricLabel}>{label}</div>
|
||||
<div className={`${styles.metricValue} ${styles[`ofstedGrade${value}`]}`}>
|
||||
@@ -611,8 +622,8 @@ export function SchoolDetailView({
|
||||
</h2>
|
||||
<p className={styles.sectionSubtitle}>
|
||||
{isSecondary
|
||||
? 'GCSE results for Year 11 pupils. National averages shown for comparison.'
|
||||
: 'End-of-primary-school tests taken by Year 6 pupils. National averages shown for comparison.'}
|
||||
? 'GCSE results for Year 11 pupils. England averages shown for comparison.'
|
||||
: 'End-of-primary-school tests taken by Year 6 pupils. England averages shown for comparison.'}
|
||||
</p>
|
||||
|
||||
{/* ── Primary / KS2 content ── */}
|
||||
@@ -637,7 +648,7 @@ export function SchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
{primaryAvg.rwm_expected_pct != null && (
|
||||
<div className={styles.heroStatHint}>National avg: {primaryAvg.rwm_expected_pct.toFixed(0)}%</div>
|
||||
<div className={styles.heroStatHint}>England avg: {primaryAvg.rwm_expected_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -659,7 +670,7 @@ export function SchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
{primaryAvg.rwm_high_pct != null && (
|
||||
<div className={styles.heroStatHint}>National avg: {primaryAvg.rwm_high_pct.toFixed(0)}%</div>
|
||||
<div className={styles.heroStatHint}>England avg: {primaryAvg.rwm_high_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -765,7 +776,7 @@ export function SchoolDetailView({
|
||||
</div>
|
||||
<div className={styles.metricValue}>{latestResults.attainment_8_score.toFixed(1)}</div>
|
||||
{secondaryAvg.attainment_8_score != null && (
|
||||
<div className={styles.metricHint}>National avg: {secondaryAvg.attainment_8_score.toFixed(1)}</div>
|
||||
<div className={styles.metricHint}>England avg: {secondaryAvg.attainment_8_score.toFixed(1)}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -789,7 +800,7 @@ export function SchoolDetailView({
|
||||
</div>
|
||||
<div className={styles.metricValue}>{formatPercentage(latestResults.english_maths_standard_pass_pct)}</div>
|
||||
{secondaryAvg.english_maths_standard_pass_pct != null && (
|
||||
<div className={styles.metricHint}>National avg: {secondaryAvg.english_maths_standard_pass_pct.toFixed(0)}%</div>
|
||||
<div className={styles.metricHint}>England avg: {secondaryAvg.english_maths_standard_pass_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -801,7 +812,7 @@ export function SchoolDetailView({
|
||||
</div>
|
||||
<div className={styles.metricValue}>{formatPercentage(latestResults.english_maths_strong_pass_pct)}</div>
|
||||
{secondaryAvg.english_maths_strong_pass_pct != null && (
|
||||
<div className={styles.metricHint}>National avg: {secondaryAvg.english_maths_strong_pass_pct.toFixed(0)}%</div>
|
||||
<div className={styles.metricHint}>England avg: {secondaryAvg.english_maths_strong_pass_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -957,7 +968,7 @@ export function SchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
{primaryAvg.eal_pct != null && (
|
||||
<div className={styles.heroStatHint}>National avg: {primaryAvg.eal_pct.toFixed(0)}%</div>
|
||||
<div className={styles.heroStatHint}>England avg: {primaryAvg.eal_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -974,7 +985,7 @@ export function SchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
{primaryAvg.sen_support_pct != null && (
|
||||
<div className={styles.heroStatHint}>National avg: {primaryAvg.sen_support_pct.toFixed(0)}%</div>
|
||||
<div className={styles.heroStatHint}>England avg: {primaryAvg.sen_support_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -1146,7 +1157,7 @@ export function SchoolDetailView({
|
||||
</div>
|
||||
<div className={styles.metricValue}>{formatPercentage(absenceData.overall_absence_rate)}</div>
|
||||
{primaryAvg.overall_absence_pct != null && (
|
||||
<div className={styles.metricHint}>National avg: ~{primaryAvg.overall_absence_pct.toFixed(1)}%</div>
|
||||
<div className={styles.metricHint}>England avg: ~{primaryAvg.overall_absence_pct.toFixed(1)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -1158,7 +1169,7 @@ export function SchoolDetailView({
|
||||
</div>
|
||||
<div className={styles.metricValue}>{formatPercentage(absenceData.persistent_absence_rate)}</div>
|
||||
{primaryAvg.persistent_absence_pct != null && (
|
||||
<div className={styles.metricHint}>National avg: ~{primaryAvg.persistent_absence_pct.toFixed(0)}%</div>
|
||||
<div className={styles.metricHint}>England avg: ~{primaryAvg.persistent_absence_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
SchoolAdmissions, SenDetail, Phonics,
|
||||
SchoolDeprivation, SchoolFinance, NationalAverages,
|
||||
} from '@/lib/types';
|
||||
import { formatPercentage, formatProgress, formatAcademicYear, formatAgeRange, isProposedToClose, ofstedLegacyAreas } from '@/lib/utils';
|
||||
import { formatPercentage, formatProgress, formatAcademicYear, formatAgeRange, isProposedToClose } from '@/lib/utils';
|
||||
import { DeltaChip } from './DeltaChip';
|
||||
import { track, getNavigationSource } from '@/lib/analytics';
|
||||
import styles from './SecondarySchoolDetailView.module.css';
|
||||
@@ -201,12 +201,17 @@ export function SecondarySchoolDetailView({
|
||||
: ofsted?.inspection_date ?? null;
|
||||
|
||||
// ── Ofsted: detect if all OEIF sub-grades match the overall ───────────
|
||||
const oeifAreas = ofsted ? ofstedLegacyAreas(ofsted) : [];
|
||||
const oeifAllSameGrade =
|
||||
!!ofsted &&
|
||||
!isReportCard &&
|
||||
oeifAreas.length >= 3 &&
|
||||
oeifAreas.every((a) => a.value === ofsted.overall_effectiveness);
|
||||
const oeifAllSameGrade = (() => {
|
||||
if (!ofsted || isReportCard) return false;
|
||||
const subs = [
|
||||
ofsted.quality_of_education,
|
||||
ofsted.behaviour_attitudes,
|
||||
ofsted.personal_development,
|
||||
ofsted.leadership_management,
|
||||
...(ofsted.early_years_provision != null ? [ofsted.early_years_provision] : []),
|
||||
].filter((v): v is number => v != null);
|
||||
return subs.length >= 3 && subs.every(v => v === ofsted.overall_effectiveness);
|
||||
})();
|
||||
|
||||
// National Attainment 8 baseline for the "Results Over Time" chart.
|
||||
const heroAtt8Nat = secondaryAvg.attainment_8_score ?? null;
|
||||
@@ -399,9 +404,7 @@ export function SecondarySchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
<p className={styles.ofstedDisclaimer}>
|
||||
{ofsted.grade_source === 'ungraded_carried_forward'
|
||||
? 'This overall grade is carried forward from an earlier inspection — Ofsted has since visited without issuing a new overall grade. From September 2024, Ofsted no longer makes an overall effectiveness judgement.'
|
||||
: 'From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections.'}
|
||||
From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections.
|
||||
</p>
|
||||
{oeifAllSameGrade ? (
|
||||
<p className={styles.ofstedAllSame}>
|
||||
@@ -409,7 +412,15 @@ export function SecondarySchoolDetailView({
|
||||
</p>
|
||||
) : (
|
||||
<div className={styles.metricsGrid}>
|
||||
{oeifAreas.map(({ label, value }) => (
|
||||
{[
|
||||
{ label: 'Quality of Teaching', value: ofsted.quality_of_education },
|
||||
{ label: 'Behaviour in School', value: ofsted.behaviour_attitudes },
|
||||
{ label: 'Pupils\' Wider Development', value: ofsted.personal_development },
|
||||
{ label: 'School Leadership', value: ofsted.leadership_management },
|
||||
...(ofsted.early_years_provision != null
|
||||
? [{ label: 'Early Years (Reception)', value: ofsted.early_years_provision }]
|
||||
: []),
|
||||
].map(({ label, value }) => value != null && (
|
||||
<div key={label} className={styles.metricCard}>
|
||||
<div className={styles.metricLabel}>{label}</div>
|
||||
<div className={`${styles.metricValue} ${styles[`ofstedGrade${value}`]}`}>
|
||||
@@ -452,7 +463,7 @@ export function SecondarySchoolDetailView({
|
||||
GCSE Results ({formatAcademicYear(latestResults.year)})
|
||||
</h2>
|
||||
<p className={styles.sectionSubtitle}>
|
||||
GCSE results for Year 11 pupils. National averages shown for comparison.
|
||||
GCSE results for Year 11 pupils. England averages shown for comparison.
|
||||
</p>
|
||||
|
||||
{p8Suspended && (
|
||||
@@ -481,7 +492,7 @@ export function SecondarySchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
{secondaryAvg.attainment_8_score != null && (
|
||||
<div className={styles.heroStatHint}>National avg: {secondaryAvg.attainment_8_score.toFixed(1)}</div>
|
||||
<div className={styles.heroStatHint}>England avg: {secondaryAvg.attainment_8_score.toFixed(1)}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -521,7 +532,7 @@ export function SecondarySchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
{secondaryAvg.english_maths_strong_pass_pct != null && (
|
||||
<div className={styles.heroStatHint}>National avg: {secondaryAvg.english_maths_strong_pass_pct.toFixed(0)}%</div>
|
||||
<div className={styles.heroStatHint}>England avg: {secondaryAvg.english_maths_strong_pass_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -543,7 +554,7 @@ export function SecondarySchoolDetailView({
|
||||
)}
|
||||
</div>
|
||||
{secondaryAvg.english_maths_standard_pass_pct != null && (
|
||||
<div className={styles.heroStatHint}>National avg: {secondaryAvg.english_maths_standard_pass_pct.toFixed(0)}%</div>
|
||||
<div className={styles.heroStatHint}>England avg: {secondaryAvg.english_maths_standard_pass_pct.toFixed(0)}%</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -680,42 +680,6 @@ export function buildSchoolSummary(
|
||||
return parts.join(', ') + '.';
|
||||
}
|
||||
|
||||
// ─── Legacy (OEIF) sub-judgement areas ────────────────────────────────────────
|
||||
|
||||
export interface OfstedLegacyArea {
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The published OEIF sub-judgement areas for the legacy Ofsted layout, in
|
||||
* display order. Only real grades (1–4) are returned: Ofsted's sentinel
|
||||
* codes for "not applicable / no judgement" (9, and any 0/8 variants) and
|
||||
* nulls are filtered out, so a cryptic "9" never renders as a rating.
|
||||
* Sixth Form provision is included where a school has one — it was
|
||||
* previously dropped from the detail grid entirely.
|
||||
*/
|
||||
export function ofstedLegacyAreas(ofsted: {
|
||||
quality_of_education?: number | null;
|
||||
behaviour_attitudes?: number | null;
|
||||
personal_development?: number | null;
|
||||
leadership_management?: number | null;
|
||||
early_years_provision?: number | null;
|
||||
sixth_form_provision?: number | null;
|
||||
}): OfstedLegacyArea[] {
|
||||
const candidates: Array<[string, number | null | undefined]> = [
|
||||
['Quality of Teaching', ofsted.quality_of_education],
|
||||
['Behaviour in School', ofsted.behaviour_attitudes],
|
||||
["Pupils' Wider Development", ofsted.personal_development],
|
||||
['School Leadership', ofsted.leadership_management],
|
||||
['Early Years (Reception)', ofsted.early_years_provision],
|
||||
['Sixth Form', ofsted.sixth_form_provision],
|
||||
];
|
||||
return candidates
|
||||
.filter((c): c is [string, number] => c[1] != null && c[1] >= 1 && c[1] <= 4)
|
||||
.map(([label, value]) => ({ label, value }));
|
||||
}
|
||||
|
||||
// ─── List-level Ofsted badge ──────────────────────────────────────────────────
|
||||
|
||||
export interface OfstedListBadge {
|
||||
|
||||
Reference in New Issue
Block a user