From 97734832210d7e5dafbfe3b40b9cf05b8e9b6acd Mon Sep 17 00:00:00 2001 From: Tudor Date: Thu, 16 Jul 2026 14:48:17 +0100 Subject: [PATCH] fix(compare): date report cards with their own inspection date, never the legacy one Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB --- backend/data_loader.py | 5 +++ backend/models.py | 3 ++ .../tests/test_supplementary_enrichment.py | 20 ++++++++++ .../components/CompareOfsted.test.tsx | 39 +++++++++++++++++++ .../components/compare/CompareOfsted.tsx | 12 +++++- nextjs-app/lib/types.ts | 2 + 6 files changed, 79 insertions(+), 2 deletions(-) diff --git a/backend/data_loader.py b/backend/data_loader.py index 332ce5e..7b01a07 100644 --- a/backend/data_loader.py +++ b/backend/data_loader.py @@ -617,6 +617,11 @@ def _ofsted_block(o, urn: int) -> dict: block = { "framework": o.framework, "inspection_date": o.inspection_date.isoformat() if o.inspection_date else None, + "rc_inspection_date": ( + o.rc_inspection_date.isoformat() + if getattr(o, "rc_inspection_date", None) + else None + ), "inspection_type": o.inspection_type, "overall_effectiveness": overall, "grade_source": grade_source, diff --git a/backend/models.py b/backend/models.py index b2325d9..1deae34 100644 --- a/backend/models.py +++ b/backend/models.py @@ -156,6 +156,9 @@ class FactOfstedInspection(Base): rc_leadership_governance = Column(Integer) rc_early_years = Column(Integer) rc_sixth_form = Column(Integer) + # Start date of the report-card inspection itself (renewed framework, + # Nov 2025+). Null for rows without report-card grades. + rc_inspection_date = Column(Date) report_url = Column(Text) diff --git a/backend/tests/test_supplementary_enrichment.py b/backend/tests/test_supplementary_enrichment.py index fe6e746..0fd5a38 100644 --- a/backend/tests/test_supplementary_enrichment.py +++ b/backend/tests/test_supplementary_enrichment.py @@ -3,6 +3,7 @@ labels, provider-page URL, graded-vs-carried-forward provenance, and the admissions preference/cross-LA detail promoted in the data-foundation PR.""" import types +from datetime import date from backend.data_loader import _admissions_row_dict, _ofsted_block @@ -40,6 +41,25 @@ def test_grade_source_graded_vs_carried_forward(): assert _ofsted_block(_row(), urn=1)["grade_source"] is None +def test_ofsted_block_carries_rc_inspection_date(): + o = _row( + ungraded_grade=2, + rc_achievement=1, + rc_inspection_date=date(2026, 2, 3), + inspection_date=date(2021, 10, 7), + ) + block = _ofsted_block(o, urn=138690) + assert block["rc_inspection_date"] == "2026-02-03" + # The legacy inspection date is still present, unchanged. + assert block["inspection_date"] == "2021-10-07" + + +def test_ofsted_block_rc_inspection_date_none_when_absent(): + o = _row(overall_effectiveness=1, inspection_date=date(2021, 10, 13)) + block = _ofsted_block(o, urn=136276) + assert block["rc_inspection_date"] is None + + def test_ofsted_block_keeps_existing_keys(): block = _ofsted_block(_row(overall_effectiveness=2, quality_of_education=2), urn=1) for key in ("framework", "inspection_date", "overall_effectiveness", diff --git a/nextjs-app/__tests__/components/CompareOfsted.test.tsx b/nextjs-app/__tests__/components/CompareOfsted.test.tsx index 849d255..912f296 100644 --- a/nextjs-app/__tests__/components/CompareOfsted.test.tsx +++ b/nextjs-app/__tests__/components/CompareOfsted.test.tsx @@ -96,6 +96,45 @@ describe('CompareOfsted', () => { expect(links[0]).toHaveAttribute('href', 'https://reports.ofsted.gov.uk/provider/21/1'); }); + it('dates a report card with the report-card inspection date, never the legacy date', () => { + const cardSchool = school(4, 'Dated Card School'); + const cardData: Record = { + '4': { + school_info: cardSchool, + yearly_data: [], + ofsted: ofsted({ + inspection_date: '2021-10-07', + rc_inspection_date: '2026-02-03', + rc_safeguarding_met: true, + report_card: { rc_achievement: { code: 1, label: 'Exceptional' } }, + }), + }, + }; + render(); + expect(screen.getByText(/3 Feb 2026/)).toBeInTheDocument(); + expect(screen.queryByText(/7 Oct 2021/)).toBeNull(); + expect(screen.queryByText('4+ years ago')).toBeNull(); + }); + + it('shows an em dash when a report card has no rc_inspection_date yet', () => { + const cardSchool = school(5, 'Undated Card School'); + const cardData: Record = { + '5': { + school_info: cardSchool, + yearly_data: [], + ofsted: ofsted({ + inspection_date: '2021-10-07', + rc_inspection_date: null, + rc_safeguarding_met: true, + report_card: { rc_achievement: { code: 1, label: 'Exceptional' } }, + }), + }, + }; + render(); + expect(screen.getByText('—')).toBeInTheDocument(); + expect(screen.queryByText(/7 Oct 2021/)).toBeNull(); + }); + it('renders a per-measure mobile tag with the short school name', () => { render(); // Each measure repeats the schools, so the short name ("Graded" from diff --git a/nextjs-app/components/compare/CompareOfsted.tsx b/nextjs-app/components/compare/CompareOfsted.tsx index 56ff359..c5486ac 100644 --- a/nextjs-app/components/compare/CompareOfsted.tsx +++ b/nextjs-app/components/compare/CompareOfsted.tsx @@ -180,10 +180,18 @@ export function CompareOfsted({ {schools.map((school, i) => { const ofsted = data[String(school.urn)]?.ofsted; - const age = yearsSince(ofsted?.inspection_date ?? null); + // A report card is dated by its OWN inspection date. The legacy + // inspection_date belongs to an older inspection and must never + // be shown against a report card (report cards exist only from + // Nov 2025). + const dateIso = + displays[i].kind === 'report_card' + ? ofsted?.rc_inspection_date ?? null + : ofsted?.inspection_date ?? null; + const age = yearsSince(dateIso); return ( - {formatInspectionDate(ofsted?.inspection_date ?? null)}{' '} + {formatInspectionDate(dateIso)}{' '} {age != null && age > 4 && 4+ years ago} ); diff --git a/nextjs-app/lib/types.ts b/nextjs-app/lib/types.ts index f82d907..b4dc009 100644 --- a/nextjs-app/lib/types.ts +++ b/nextjs-app/lib/types.ts @@ -79,6 +79,8 @@ export interface School { export interface OfstedInspection { framework: 'OEIF' | 'ReportCard' | null; inspection_date: string | null; + /** Start date of the report-card inspection itself (Nov 2025+); null otherwise. */ + rc_inspection_date?: string | null; inspection_type: string | null; // OEIF fields (old framework, pre-Nov 2025) overall_effectiveness: 1 | 2 | 3 | 4 | null;