diff --git a/nextjs-app/__tests__/components/SchoolRow.test.tsx b/nextjs-app/__tests__/components/SchoolRow.test.tsx
index 1911d31..46f9dcb 100644
--- a/nextjs-app/__tests__/components/SchoolRow.test.tsx
+++ b/nextjs-app/__tests__/components/SchoolRow.test.tsx
@@ -52,3 +52,8 @@ describe('SchoolRow shares the school page flags', () => {
expect(screen.getByText("Boys' school")).toBeInTheDocument();
});
});
+
+it('renders no Ofsted badge for a school with no inspection data (audit H4)', () => {
+ const { container } = render();
+ expect(container.textContent).not.toMatch(/Not yet inspected|Inspected/);
+});
diff --git a/nextjs-app/__tests__/lib/ofstedStatus.test.ts b/nextjs-app/__tests__/lib/ofstedStatus.test.ts
index 86deadc..b19b4cc 100644
--- a/nextjs-app/__tests__/lib/ofstedStatus.test.ts
+++ b/nextjs-app/__tests__/lib/ofstedStatus.test.ts
@@ -1,4 +1,4 @@
-import { gradeSourceLine, latestVisitLine, showLatestVisitLine } from '@/lib/ofstedStatus';
+import { gradeSourceLine, hasReportCard, latestVisitLine, showLatestVisitLine } from '@/lib/ofstedStatus';
import type { OfstedInspection } from '@/lib/types';
const base = { report_card: {} } as unknown as OfstedInspection;
@@ -45,3 +45,12 @@ describe('showLatestVisitLine', () => {
latest_visit: { date: '2025-06-17', kind: 'graded', outcome: null } })).toBe(false);
});
});
+
+it('names a short inspection as Ofsted does', () => {
+ expect(latestVisitLine({ date: '2023-02-01', kind: 'short', outcome: null })).toBe('Short inspection, 1 February 2023');
+});
+
+it('treats a report card with only a safeguarding result as a report card (FE)', () => {
+ expect(hasReportCard({ rc_inspection_date: '2026-05-12', report_card: {} })).toBe(true);
+ expect(hasReportCard({ rc_inspection_date: null, report_card: {} })).toBe(false);
+});
diff --git a/nextjs-app/__tests__/lib/utils.test.ts b/nextjs-app/__tests__/lib/utils.test.ts
index ea9f983..a24d9c3 100644
--- a/nextjs-app/__tests__/lib/utils.test.ts
+++ b/nextjs-app/__tests__/lib/utils.test.ts
@@ -148,40 +148,40 @@ describe('ofstedLegacyAreas', () => {
describe('buildOfstedListBadge', () => {
it('returns grade word + year for OEIF Outstanding', () => {
const badge = buildOfstedListBadge({ ofsted_grade: 1, ofsted_grade_date: '2023-11-15', ofsted_date: '2023-11-15', ofsted_framework: 'OEIF' });
- expect(badge.label).toBe('Outstanding · 2023');
- expect(badge.cssClass).toBe('ofsted1');
+ expect(badge!.label).toBe('Outstanding · 2023');
+ expect(badge!.cssClass).toBe('ofsted1');
});
it('returns grade word for each OEIF grade', () => {
- expect(buildOfstedListBadge({ ofsted_grade: 2, ofsted_grade_date: '2022-05-01' }).label).toBe('Good · 2022');
- expect(buildOfstedListBadge({ ofsted_grade: 3, ofsted_grade_date: '2021-01-01' }).label).toBe('Req. Improvement · 2021');
- expect(buildOfstedListBadge({ ofsted_grade: 4, ofsted_grade_date: '2020-03-01' }).label).toBe('Inadequate · 2020');
+ expect(buildOfstedListBadge({ ofsted_grade: 2, ofsted_grade_date: '2022-05-01' })!.label).toBe('Good · 2022');
+ expect(buildOfstedListBadge({ ofsted_grade: 3, ofsted_grade_date: '2021-01-01' })!.label).toBe('Req. Improvement · 2021');
+ expect(buildOfstedListBadge({ ofsted_grade: 4, ofsted_grade_date: '2020-03-01' })!.label).toBe('Inadequate · 2020');
});
it('prints a grade without a year when its date is missing', () => {
const badge = buildOfstedListBadge({ ofsted_grade: 2, ofsted_grade_date: null, ofsted_date: '2025-01-01' });
- expect(badge.label).toBe('Good');
- expect(badge.cssClass).toBe('ofsted2');
+ expect(badge!.label).toBe('Good');
+ expect(badge!.cssClass).toBe('ofsted2');
});
it('dates a grade by the inspection that awarded it, not the latest visit', () => {
// Washwood Heath: Good from a March 2020 graded inspection; latest visit
// an ungraded one in May 2025 (audit M1).
const badge = buildOfstedListBadge({ ofsted_grade: 2, ofsted_grade_date: '2020-03-03', ofsted_date: '2025-05-21' });
- expect(badge.label).toBe('Good · 2020');
+ expect(badge!.label).toBe('Good · 2020');
});
it('shows Inspected for a latest inspection that gave no grade (Rabbsfarm)', () => {
// Audit C1: this used to read "Good · 2025", from a 2020 ungraded visit.
const badge = buildOfstedListBadge({ ofsted_grade: null, ofsted_grade_date: null, ofsted_date: '2025-06-17' });
- expect(badge.label).toBe('Inspected · 2025');
- expect(badge.cssClass).toBe('ofstedInspected');
+ expect(badge!.label).toBe('Inspected · 2025');
+ expect(badge!.cssClass).toBe('ofstedInspected');
});
it('returns a Report Card badge when ofsted_rc_date is present', () => {
const badge = buildOfstedListBadge({ ofsted_grade: null, ofsted_rc_date: '2026-02-03' });
- expect(badge.label).toBe('Report Card · 2026');
- expect(badge.cssClass).toBe('ofstedRc');
+ expect(badge!.label).toBe('Report Card · 2026');
+ expect(badge!.cssClass).toBe('ofstedRc');
});
it('a report card wins over a carried-forward legacy grade', () => {
@@ -194,34 +194,46 @@ describe('buildOfstedListBadge', () => {
ofsted_framework: 'Schools - S5',
ofsted_rc_date: '2026-02-03',
});
- expect(badge.label).toBe('Report Card · 2026');
- expect(badge.cssClass).toBe('ofstedRc');
+ expect(badge!.label).toBe('Report Card · 2026');
+ expect(badge!.cssClass).toBe('ofstedRc');
});
it('returns an "Inspected" badge for an OEIF inspection with no overall grade (post-Sept-2024)', () => {
// Inspected after Sept 2024: inspection on record (date + framework) but
// Ofsted no longer issues an overall grade. Must NOT read as "Not yet inspected".
const badge = buildOfstedListBadge({ ofsted_grade: null, ofsted_date: '2024-11-01', ofsted_framework: 'OEIF' });
- expect(badge.label).toBe('Inspected · 2024');
- expect(badge.cssClass).toBe('ofstedInspected');
+ expect(badge!.label).toBe('Inspected · 2024');
+ expect(badge!.cssClass).toBe('ofstedInspected');
});
it('returns an "Inspected" badge without a year when the grade is missing and date is absent but a record exists', () => {
const badge = buildOfstedListBadge({ ofsted_grade: null, ofsted_date: null, ofsted_framework: 'OEIF' });
- expect(badge.label).toBe('Inspected');
- expect(badge.cssClass).toBe('ofstedInspected');
+ expect(badge!.label).toBe('Inspected');
+ expect(badge!.cssClass).toBe('ofstedInspected');
});
- it('returns pending badge when no grade and no inspection on record', () => {
- const badge = buildOfstedListBadge({ ofsted_grade: null, ofsted_date: null, ofsted_framework: null });
- expect(badge.label).toBe('Not yet inspected');
- expect(badge.cssClass).toBe('ofstedPending');
+ it('says "Not yet inspected" only when Ofsted lists the school uninspected (audit H4)', () => {
+ const badge = buildOfstedListBadge({ ofsted_grade: null, ofsted_date: null, ofsted_listed: true });
+ expect(badge).toEqual({ label: 'Not yet inspected', cssClass: 'ofstedPending' });
});
- it('returns pending badge when all fields are undefined', () => {
- const badge = buildOfstedListBadge({});
- expect(badge.label).toBe('Not yet inspected');
- expect(badge.cssClass).toBe('ofstedPending');
+ it('names ISI for a school GIAS says ISI inspects', () => {
+ expect(buildOfstedListBadge({ ofsted_date: null, inspectorate: 'ISI', ofsted_listed: false }))
+ .toEqual({ label: 'Inspected by ISI', cssClass: 'ofstedPending' });
+ });
+
+ it('an Ofsted record wins over ISI', () => {
+ expect(buildOfstedListBadge({ ofsted_grade: 2, ofsted_grade_date: '2022-03-01', inspectorate: 'ISI' })!.label)
+ .toBe('Good · 2022');
+ });
+
+ it('gives no badge without any inspection data', () => {
+ expect(buildOfstedListBadge({ ofsted_grade: null, ofsted_date: null, ofsted_framework: null })).toBeNull();
+ expect(buildOfstedListBadge({ ofsted_listed: null, inspectorate: 'Not applicable' })).toBeNull();
+ });
+
+ it('gives no badge when all fields are undefined (audit H4)', () => {
+ expect(buildOfstedListBadge({})).toBeNull();
});
});
diff --git a/nextjs-app/components/LeafletMapInner.tsx b/nextjs-app/components/LeafletMapInner.tsx
index ded0513..c8a36a7 100644
--- a/nextjs-app/components/LeafletMapInner.tsx
+++ b/nextjs-app/components/LeafletMapInner.tsx
@@ -104,7 +104,7 @@ function cardHtml(school: School, ctx: CardContext): string {
? `${metric ? ' · ' : ''}${school.total_pupils.toLocaleString('en-GB')} pupils` : '';
return `
-
${escapeHtml(badge.label)}${type}
+
${badge ? `${escapeHtml(badge.label)}` : ''}${type}
${metric || pupils ? `
${metric}${pupils}
` : ''}
View
diff --git a/nextjs-app/components/SchoolRow.tsx b/nextjs-app/components/SchoolRow.tsx
index f250975..578b90c 100644
--- a/nextjs-app/components/SchoolRow.tsx
+++ b/nextjs-app/components/SchoolRow.tsx
@@ -66,9 +66,11 @@ export function SchoolRow({
{school.school_name}
-
- {ofstedBadge.label}
-
+ {ofstedBadge && (
+
+ {ofstedBadge.label}
+
+ )}
{/* Line 2: Context tags */}
diff --git a/nextjs-app/components/SecondarySchoolRow.tsx b/nextjs-app/components/SecondarySchoolRow.tsx
index bc3f48e..da5fa8f 100644
--- a/nextjs-app/components/SecondarySchoolRow.tsx
+++ b/nextjs-app/components/SecondarySchoolRow.tsx
@@ -67,9 +67,11 @@ export function SecondarySchoolRow({
{school.school_name}
-
- {ofstedBadge.label}
-
+ {ofstedBadge && (
+
+ {ofstedBadge.label}
+
+ )}
{/* Line 2: Context tags */}
diff --git a/nextjs-app/lib/ofstedStatus.ts b/nextjs-app/lib/ofstedStatus.ts
index f9164a5..bdf02b8 100644
--- a/nextjs-app/lib/ofstedStatus.ts
+++ b/nextjs-app/lib/ofstedStatus.ts
@@ -17,8 +17,22 @@ const VISIT_KIND: Record = {
report_card: 'Report card inspection',
graded: 'Graded inspection',
ungraded: 'Ungraded inspection',
+ short: 'Short inspection',
};
+/** fact_ofsted_latest.framework for Ofsted's further education and skills records (audit H4). */
+export const FE_FRAMEWORK = 'Further education and skills';
+
+/**
+ * A report card is in force. Checked by its date as well as its area labels:
+ * an FE report card carries a safeguarding result and no school areas.
+ */
+export function hasReportCard(
+ ofsted: Pick,
+): boolean {
+ return !!ofsted.rc_inspection_date || Object.keys(ofsted.report_card ?? {}).length > 0;
+}
+
/** "Graded inspection, 6 July 2016" or "Confirmed at an ungraded inspection, 14 March 2023". */
export function gradeSourceLine(current: OfstedCurrentGrade): string {
const when = formatOfstedDate(current.date);
diff --git a/nextjs-app/lib/types.ts b/nextjs-app/lib/types.ts
index 39ab734..d90adb7 100644
--- a/nextjs-app/lib/types.ts
+++ b/nextjs-app/lib/types.ts
@@ -86,6 +86,10 @@ export interface School {
/** The school's latest inspection of any kind. */
ofsted_date?: string | null;
ofsted_framework?: string | null;
+ /** The inspectorate GIAS names ("ISI", "Ofsted", "Not applicable"). Audit H4. */
+ inspectorate?: string | null;
+ /** Whether Ofsted's own data lists the school, inspected or not. Audit H4. */
+ ofsted_listed?: boolean | null;
}
// ============================================================================
@@ -143,7 +147,7 @@ export interface OfstedCurrentGrade {
export interface OfstedLatestVisit {
date: string;
- kind: 'report_card' | 'graded' | 'ungraded';
+ kind: 'report_card' | 'graded' | 'ungraded' | 'short';
outcome: string | null;
}
diff --git a/nextjs-app/lib/utils.ts b/nextjs-app/lib/utils.ts
index 9d3440d..b9ae6e4 100644
--- a/nextjs-app/lib/utils.ts
+++ b/nextjs-app/lib/utils.ts
@@ -678,7 +678,9 @@ export interface OfstedListBadge {
* or an ungraded visit whose outcome names no grade): "Inspected · YYYY",
* dated by the latest visit, so a school never reads as both inspected and
* "Not yet inspected"
- * - No inspection on record: "Not yet inspected" in grey
+ * - No inspection on record (audit H4): "Inspected by ISI" when GIAS names
+ * ISI; "Not yet inspected" only when Ofsted's own data lists the school;
+ * otherwise no badge, never a claim the data cannot support.
*/
export function buildOfstedListBadge(school: {
ofsted_grade?: 1 | 2 | 3 | 4 | null;
@@ -686,7 +688,9 @@ export function buildOfstedListBadge(school: {
ofsted_date?: string | null;
ofsted_framework?: string | null;
ofsted_rc_date?: string | null;
-}): OfstedListBadge {
+ inspectorate?: string | null;
+ ofsted_listed?: boolean | null;
+}): OfstedListBadge | null {
// A report card wins over any carried-forward legacy grade — signalled by
// ofsted_rc_date. ofsted_framework is the raw event grouping ("Schools -
// S5"), never "ReportCard", so it can't detect report cards.
@@ -719,7 +723,9 @@ export function buildOfstedListBadge(school: {
return { label: `Inspected${visitYear ? ` · ${visitYear}` : ''}`, cssClass: 'ofstedInspected' };
}
- return { label: 'Not yet inspected', cssClass: 'ofstedPending' };
+ if (school.inspectorate === 'ISI') return { label: 'Inspected by ISI', cssClass: 'ofstedPending' };
+ if (school.ofsted_listed) return { label: 'Not yet inspected', cssClass: 'ofstedPending' };
+ return null;
}
// ============================================================================