feat(ofsted): add Report Card system support alongside legacy OEIF grades
All checks were successful
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 47s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 1m11s
Build and Push Docker Images / Build Integrator (push) Successful in 58s
Build and Push Docker Images / Build Kestra Init (push) Successful in 32s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
All checks were successful
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 47s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 1m11s
Build and Push Docker Images / Build Integrator (push) Successful in 58s
Build and Push Docker Images / Build Kestra Init (push) Successful in 32s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
Ofsted replaced single overall grades with Report Cards from Nov 2025. Both systems are retained during the transition period. - DB: new framework + 9 RC columns on ofsted_inspections (schema v4) - Integrator: auto-detect OEIF vs Report Card from CSV column headers; parse 5-level RC grades and safeguarding met/not-met - API: expose all new fields in the ofsted response dict - Frontend: branch on framework='ReportCard' to show safeguarding badge + 8-category grid; fall back to legacy OEIF layout otherwise; always show inspection date in both layouts - CSS: rcGrade1–5 and safeguardingMet/NotMet classes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -575,6 +575,10 @@ def get_supplementary_data(db: Session, urn: int) -> dict:
|
||||
# Ofsted inspection
|
||||
o = safe_query(OfstedInspection, "urn")
|
||||
result["ofsted"] = {
|
||||
"framework": o.framework,
|
||||
"inspection_date": o.inspection_date.isoformat() if o.inspection_date else None,
|
||||
"inspection_type": o.inspection_type,
|
||||
# OEIF fields (old framework)
|
||||
"overall_effectiveness": o.overall_effectiveness,
|
||||
"quality_of_education": o.quality_of_education,
|
||||
"behaviour_attitudes": o.behaviour_attitudes,
|
||||
@@ -582,8 +586,16 @@ def get_supplementary_data(db: Session, urn: int) -> dict:
|
||||
"leadership_management": o.leadership_management,
|
||||
"early_years_provision": o.early_years_provision,
|
||||
"previous_overall": o.previous_overall,
|
||||
"inspection_date": o.inspection_date.isoformat() if o.inspection_date else None,
|
||||
"inspection_type": o.inspection_type,
|
||||
# Report Card fields (new framework, from Nov 2025)
|
||||
"rc_safeguarding_met": o.rc_safeguarding_met,
|
||||
"rc_inclusion": o.rc_inclusion,
|
||||
"rc_curriculum_teaching": o.rc_curriculum_teaching,
|
||||
"rc_achievement": o.rc_achievement,
|
||||
"rc_attendance_behaviour": o.rc_attendance_behaviour,
|
||||
"rc_personal_development": o.rc_personal_development,
|
||||
"rc_leadership_governance": o.rc_leadership_governance,
|
||||
"rc_early_years": o.rc_early_years,
|
||||
"rc_sixth_form": o.rc_sixth_form,
|
||||
} if o else None
|
||||
|
||||
# Parent View
|
||||
|
||||
@@ -171,6 +171,10 @@ class OfstedInspection(Base):
|
||||
inspection_date = Column(Date)
|
||||
publication_date = Column(Date)
|
||||
inspection_type = Column(String(100)) # Section 5 / Section 8 etc.
|
||||
# Which inspection framework was used: 'OEIF' or 'ReportCard'
|
||||
framework = Column(String(20))
|
||||
|
||||
# --- OEIF grades (old framework, pre-Nov 2025) ---
|
||||
# 1=Outstanding 2=Good 3=Requires improvement 4=Inadequate
|
||||
overall_effectiveness = Column(Integer)
|
||||
quality_of_education = Column(Integer)
|
||||
@@ -180,8 +184,20 @@ class OfstedInspection(Base):
|
||||
early_years_provision = Column(Integer) # nullable — not all schools
|
||||
previous_overall = Column(Integer) # for trend display
|
||||
|
||||
# --- Report Card grades (new framework, from Nov 2025) ---
|
||||
# 1=Exceptional 2=Strong 3=Expected standard 4=Needs attention 5=Urgent improvement
|
||||
rc_safeguarding_met = Column(Boolean) # True=Met, False=Not met
|
||||
rc_inclusion = Column(Integer)
|
||||
rc_curriculum_teaching = Column(Integer)
|
||||
rc_achievement = Column(Integer)
|
||||
rc_attendance_behaviour = Column(Integer)
|
||||
rc_personal_development = Column(Integer)
|
||||
rc_leadership_governance = Column(Integer)
|
||||
rc_early_years = Column(Integer) # nullable — not all schools
|
||||
rc_sixth_form = Column(Integer) # nullable — secondary only
|
||||
|
||||
def __repr__(self):
|
||||
return f"<OfstedInspection(urn={self.urn}, overall={self.overall_effectiveness})>"
|
||||
return f"<OfstedInspection(urn={self.urn}, framework={self.framework}, overall={self.overall_effectiveness})>"
|
||||
|
||||
|
||||
class OfstedParentView(Base):
|
||||
|
||||
@@ -13,11 +13,12 @@ WHEN TO BUMP:
|
||||
"""
|
||||
|
||||
# Current schema version - increment when models change
|
||||
SCHEMA_VERSION = 3
|
||||
SCHEMA_VERSION = 4
|
||||
|
||||
# Changelog for documentation
|
||||
SCHEMA_CHANGELOG = {
|
||||
1: "Initial schema with School and SchoolResult tables",
|
||||
2: "Added pupil absence fields (reading, maths, gps, writing, science)",
|
||||
3: "Added supplementary data tables: ofsted, parent_view, census, admissions, sen_detail, phonics, deprivation, finance; GIAS columns on schools",
|
||||
4: "Added Ofsted Report Card columns to ofsted_inspections (new framework from Nov 2025)",
|
||||
}
|
||||
|
||||
@@ -81,6 +81,65 @@ GRADE_MAP = {
|
||||
"Inadequate": 4, "4": 4, 4: 4,
|
||||
}
|
||||
|
||||
# Report Card grade text → integer (1=Exceptional … 5=Urgent improvement)
|
||||
RC_GRADE_MAP = {
|
||||
"exceptional": 1,
|
||||
"strong standard": 2,
|
||||
"strong": 2,
|
||||
"expected standard": 3,
|
||||
"expected": 3,
|
||||
"needs attention": 4,
|
||||
"urgent improvement": 5,
|
||||
}
|
||||
|
||||
# Column name priority for Report Card fields (best-guess names; Ofsted may vary)
|
||||
RC_COLUMN_PRIORITY = {
|
||||
"rc_safeguarding": [
|
||||
"Safeguarding",
|
||||
"safeguarding",
|
||||
"Safeguarding standards",
|
||||
],
|
||||
"rc_inclusion": [
|
||||
"Inclusion",
|
||||
"inclusion",
|
||||
],
|
||||
"rc_curriculum_teaching": [
|
||||
"Curriculum and teaching",
|
||||
"curriculum_and_teaching",
|
||||
"Curriculum & teaching",
|
||||
],
|
||||
"rc_achievement": [
|
||||
"Achievement",
|
||||
"achievement",
|
||||
],
|
||||
"rc_attendance_behaviour": [
|
||||
"Attendance and behaviour",
|
||||
"attendance_and_behaviour",
|
||||
"Attendance & behaviour",
|
||||
],
|
||||
"rc_personal_development": [
|
||||
"Personal development and well-being",
|
||||
"Personal development and wellbeing",
|
||||
"personal_development_and_wellbeing",
|
||||
"Personal development & well-being",
|
||||
],
|
||||
"rc_leadership_governance": [
|
||||
"Leadership and governance",
|
||||
"leadership_and_governance",
|
||||
"Leadership & governance",
|
||||
],
|
||||
"rc_early_years": [
|
||||
"Early years",
|
||||
"early_years",
|
||||
"Early years provision",
|
||||
],
|
||||
"rc_sixth_form": [
|
||||
"Sixth form",
|
||||
"sixth_form",
|
||||
"Sixth form in schools",
|
||||
],
|
||||
}
|
||||
|
||||
DEST_DIR = SUPPLEMENTARY_DIR / "ofsted"
|
||||
|
||||
|
||||
@@ -137,6 +196,26 @@ def _parse_grade(val) -> int | None:
|
||||
return GRADE_MAP.get(key)
|
||||
|
||||
|
||||
def _parse_rc_grade(val) -> int | None:
|
||||
"""Parse a Report Card grade text to integer 1–5."""
|
||||
if pd.isna(val):
|
||||
return None
|
||||
key = str(val).strip().lower()
|
||||
return RC_GRADE_MAP.get(key)
|
||||
|
||||
|
||||
def _parse_safeguarding(val) -> bool | None:
|
||||
"""Parse safeguarding 'Met'/'Not met' to boolean."""
|
||||
if pd.isna(val):
|
||||
return None
|
||||
s = str(val).strip().lower()
|
||||
if s == "met":
|
||||
return True
|
||||
if s in ("not met", "not_met"):
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def _parse_date(val) -> date | None:
|
||||
if pd.isna(val):
|
||||
return None
|
||||
@@ -148,6 +227,19 @@ def _parse_date(val) -> date | None:
|
||||
return None
|
||||
|
||||
|
||||
def _detect_framework(df: pd.DataFrame) -> str:
|
||||
"""Return 'ReportCard' if new-format columns are present, else 'OEIF'."""
|
||||
rc_indicators = [
|
||||
"inclusion", "curriculum and teaching", "achievement",
|
||||
"attendance and behaviour", "safeguarding standards", "safeguarding",
|
||||
]
|
||||
cols_lower = {c.lower() for c in df.columns}
|
||||
for indicator in rc_indicators:
|
||||
if any(indicator in c for c in cols_lower):
|
||||
return "ReportCard"
|
||||
return "OEIF"
|
||||
|
||||
|
||||
def load(path: Path | None = None, data_dir: Path | None = None) -> dict:
|
||||
if path is None:
|
||||
dest = (data_dir / "supplementary" / "ofsted") if data_dir else DEST_DIR
|
||||
@@ -186,7 +278,11 @@ def load(path: Path | None = None, data_dir: Path | None = None) -> dict:
|
||||
hdr = _find_header_row(path)
|
||||
df = pd.read_csv(path, encoding="latin-1", low_memory=False, header=hdr)
|
||||
|
||||
# Normalise column names: for each target field pick the first source column present
|
||||
# Detect which framework the CSV represents BEFORE any renaming
|
||||
framework = _detect_framework(df)
|
||||
print(f" Ofsted: detected framework '{framework}'")
|
||||
|
||||
# Normalise OEIF column names: for each target field pick the first source column present
|
||||
available = set(df.columns)
|
||||
for target, sources in COLUMN_PRIORITY.items():
|
||||
for src in sources:
|
||||
@@ -194,6 +290,14 @@ def load(path: Path | None = None, data_dir: Path | None = None) -> dict:
|
||||
df.rename(columns={src: target}, inplace=True)
|
||||
break
|
||||
|
||||
# Normalise Report Card column names (if present)
|
||||
available = set(df.columns)
|
||||
for target, sources in RC_COLUMN_PRIORITY.items():
|
||||
for src in sources:
|
||||
if src in available:
|
||||
df.rename(columns={src: target}, inplace=True)
|
||||
break
|
||||
|
||||
if "urn" not in df.columns:
|
||||
raise ValueError(f"URN column not found. Available: {list(df.columns)[:20]}")
|
||||
|
||||
@@ -210,14 +314,18 @@ def load(path: Path | None = None, data_dir: Path | None = None) -> dict:
|
||||
df["_date_parsed"] = df["inspection_date"].apply(_parse_date)
|
||||
df = df.sort_values("_date_parsed", ascending=False).groupby("urn").first().reset_index()
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
for _, row in df.iterrows():
|
||||
urn = int(row["urn"])
|
||||
|
||||
record = {
|
||||
"urn": urn,
|
||||
"framework": framework,
|
||||
"inspection_date": _parse_date(row.get("inspection_date")),
|
||||
"publication_date": _parse_date(row.get("publication_date")),
|
||||
"inspection_type": str(row.get("inspection_type", "")).strip() or None,
|
||||
# OEIF fields
|
||||
"overall_effectiveness": _parse_grade(row.get("overall_effectiveness")),
|
||||
"quality_of_education": _parse_grade(row.get("quality_of_education")),
|
||||
"behaviour_attitudes": _parse_grade(row.get("behaviour_attitudes")),
|
||||
@@ -225,23 +333,39 @@ def load(path: Path | None = None, data_dir: Path | None = None) -> dict:
|
||||
"leadership_management": _parse_grade(row.get("leadership_management")),
|
||||
"early_years_provision": _parse_grade(row.get("early_years_provision")),
|
||||
"previous_overall": None,
|
||||
# Report Card fields
|
||||
"rc_safeguarding_met": _parse_safeguarding(row.get("rc_safeguarding")),
|
||||
"rc_inclusion": _parse_rc_grade(row.get("rc_inclusion")),
|
||||
"rc_curriculum_teaching": _parse_rc_grade(row.get("rc_curriculum_teaching")),
|
||||
"rc_achievement": _parse_rc_grade(row.get("rc_achievement")),
|
||||
"rc_attendance_behaviour": _parse_rc_grade(row.get("rc_attendance_behaviour")),
|
||||
"rc_personal_development": _parse_rc_grade(row.get("rc_personal_development")),
|
||||
"rc_leadership_governance": _parse_rc_grade(row.get("rc_leadership_governance")),
|
||||
"rc_early_years": _parse_rc_grade(row.get("rc_early_years")),
|
||||
"rc_sixth_form": _parse_rc_grade(row.get("rc_sixth_form")),
|
||||
}
|
||||
|
||||
from sqlalchemy import text
|
||||
session.execute(
|
||||
text("""
|
||||
INSERT INTO ofsted_inspections
|
||||
(urn, inspection_date, publication_date, inspection_type,
|
||||
(urn, framework, inspection_date, publication_date, inspection_type,
|
||||
overall_effectiveness, quality_of_education, behaviour_attitudes,
|
||||
personal_development, leadership_management, early_years_provision,
|
||||
previous_overall)
|
||||
previous_overall,
|
||||
rc_safeguarding_met, rc_inclusion, rc_curriculum_teaching,
|
||||
rc_achievement, rc_attendance_behaviour, rc_personal_development,
|
||||
rc_leadership_governance, rc_early_years, rc_sixth_form)
|
||||
VALUES
|
||||
(:urn, :inspection_date, :publication_date, :inspection_type,
|
||||
(:urn, :framework, :inspection_date, :publication_date, :inspection_type,
|
||||
:overall_effectiveness, :quality_of_education, :behaviour_attitudes,
|
||||
:personal_development, :leadership_management, :early_years_provision,
|
||||
:previous_overall)
|
||||
:previous_overall,
|
||||
:rc_safeguarding_met, :rc_inclusion, :rc_curriculum_teaching,
|
||||
:rc_achievement, :rc_attendance_behaviour, :rc_personal_development,
|
||||
:rc_leadership_governance, :rc_early_years, :rc_sixth_form)
|
||||
ON CONFLICT (urn) DO UPDATE SET
|
||||
previous_overall = ofsted_inspections.overall_effectiveness,
|
||||
framework = EXCLUDED.framework,
|
||||
inspection_date = EXCLUDED.inspection_date,
|
||||
publication_date = EXCLUDED.publication_date,
|
||||
inspection_type = EXCLUDED.inspection_type,
|
||||
@@ -250,7 +374,16 @@ def load(path: Path | None = None, data_dir: Path | None = None) -> dict:
|
||||
behaviour_attitudes = EXCLUDED.behaviour_attitudes,
|
||||
personal_development = EXCLUDED.personal_development,
|
||||
leadership_management = EXCLUDED.leadership_management,
|
||||
early_years_provision = EXCLUDED.early_years_provision
|
||||
early_years_provision = EXCLUDED.early_years_provision,
|
||||
rc_safeguarding_met = EXCLUDED.rc_safeguarding_met,
|
||||
rc_inclusion = EXCLUDED.rc_inclusion,
|
||||
rc_curriculum_teaching = EXCLUDED.rc_curriculum_teaching,
|
||||
rc_achievement = EXCLUDED.rc_achievement,
|
||||
rc_attendance_behaviour = EXCLUDED.rc_attendance_behaviour,
|
||||
rc_personal_development = EXCLUDED.rc_personal_development,
|
||||
rc_leadership_governance = EXCLUDED.rc_leadership_governance,
|
||||
rc_early_years = EXCLUDED.rc_early_years,
|
||||
rc_sixth_form = EXCLUDED.rc_sixth_form
|
||||
"""),
|
||||
record,
|
||||
)
|
||||
|
||||
@@ -15,8 +15,7 @@ export function Footer() {
|
||||
<div className={styles.section}>
|
||||
<h3 className={styles.title}>SchoolCompare</h3>
|
||||
<p className={styles.description}>
|
||||
Compare primary school KS2 performance across England. Data sourced from UK Government
|
||||
Compare School Performance.
|
||||
Compare primary schools across England.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -24,14 +23,6 @@ export function Footer() {
|
||||
<h4 className={styles.sectionTitle}>About</h4>
|
||||
<ul className={styles.links}>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.compare-school-performance.service.gov.uk/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.link}
|
||||
>
|
||||
Data Source
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -475,6 +475,44 @@
|
||||
.ofstedGrade3 { background: rgba(201, 162, 39, 0.15); color: #b8920e; }
|
||||
.ofstedGrade4 { background: rgba(224, 114, 86, 0.15); color: var(--accent-coral, #e07256); }
|
||||
|
||||
/* Report Card grade colours (5-level scale, lower = better) */
|
||||
.rcGrade1 { background: rgba(45, 125, 125, 0.12); color: var(--accent-teal, #2d7d7d); } /* Exceptional */
|
||||
.rcGrade2 { background: rgba(60, 140, 60, 0.12); color: #3c8c3c; } /* Strong */
|
||||
.rcGrade3 { background: rgba(201, 162, 39, 0.15); color: #b8920e; } /* Expected standard */
|
||||
.rcGrade4 { background: rgba(249, 115, 22, 0.12); color: #c2410c; } /* Needs attention */
|
||||
.rcGrade5 { background: rgba(224, 114, 86, 0.15); color: var(--accent-coral, #e07256); } /* Urgent improvement */
|
||||
|
||||
/* Safeguarding badge */
|
||||
.safeguardingRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.safeguardingLabel {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary, #1a1612);
|
||||
}
|
||||
.safeguardingMet {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
background: rgba(45, 125, 125, 0.12);
|
||||
color: var(--accent-teal, #2d7d7d);
|
||||
}
|
||||
.safeguardingNotMet {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 700;
|
||||
background: rgba(224, 114, 86, 0.15);
|
||||
color: var(--accent-coral, #e07256);
|
||||
}
|
||||
|
||||
.ofstedDisclaimer {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted, #8a847a);
|
||||
|
||||
@@ -22,6 +22,21 @@ const OFSTED_LABELS: Record<number, string> = {
|
||||
1: 'Outstanding', 2: 'Good', 3: 'Requires Improvement', 4: 'Inadequate',
|
||||
};
|
||||
|
||||
const RC_LABELS: Record<number, string> = {
|
||||
1: 'Exceptional', 2: 'Strong', 3: 'Expected standard', 4: 'Needs attention', 5: 'Urgent improvement',
|
||||
};
|
||||
|
||||
const RC_CATEGORIES = [
|
||||
{ key: 'rc_inclusion' as const, label: 'Inclusion' },
|
||||
{ key: 'rc_curriculum_teaching' as const, label: 'Curriculum & Teaching' },
|
||||
{ key: 'rc_achievement' as const, label: 'Achievement' },
|
||||
{ key: 'rc_attendance_behaviour' as const, label: 'Attendance & Behaviour' },
|
||||
{ key: 'rc_personal_development' as const, label: 'Personal Development' },
|
||||
{ key: 'rc_leadership_governance' as const, label: 'Leadership & Governance' },
|
||||
{ key: 'rc_early_years' as const, label: 'Early Years' },
|
||||
{ key: 'rc_sixth_form' as const, label: 'Sixth Form' },
|
||||
];
|
||||
|
||||
// 2023 national averages for context
|
||||
const NATIONAL_AVG = {
|
||||
rwm_expected: 60,
|
||||
@@ -179,11 +194,11 @@ export function SchoolDetailView({
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Ofsted Rating */}
|
||||
{/* Ofsted Rating / Report Card */}
|
||||
{ofsted && (
|
||||
<section id="ofsted" className={styles.card}>
|
||||
<h2 className={styles.sectionTitle}>
|
||||
Ofsted Rating
|
||||
{ofsted.framework === 'ReportCard' ? 'Ofsted Report Card' : 'Ofsted Rating'}
|
||||
{ofsted.inspection_date && (
|
||||
<span className={styles.ofstedDate}>
|
||||
Inspected {new Date(ofsted.inspection_date).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })}
|
||||
@@ -198,6 +213,43 @@ export function SchoolDetailView({
|
||||
Full report ↗
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
{ofsted.framework === 'ReportCard' ? (
|
||||
/* ── New Report Card layout ── */
|
||||
<>
|
||||
<p className={styles.ofstedDisclaimer}>
|
||||
From November 2025, Ofsted replaced single overall grades with Report Cards rating schools across several areas.
|
||||
</p>
|
||||
{ofsted.rc_safeguarding_met != null && (
|
||||
<div className={styles.safeguardingRow}>
|
||||
<span className={styles.safeguardingLabel}>Safeguarding</span>
|
||||
<span className={ofsted.rc_safeguarding_met ? styles.safeguardingMet : styles.safeguardingNotMet}>
|
||||
{ofsted.rc_safeguarding_met ? 'Met' : 'Not met'}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.metricsGrid}>
|
||||
{RC_CATEGORIES.map(({ key, label }) => {
|
||||
const value = ofsted[key] as number | null;
|
||||
return value != null ? (
|
||||
<div key={key} className={styles.metricCard}>
|
||||
<div className={styles.metricLabel}>{label}</div>
|
||||
<div className={`${styles.metricValue} ${styles[`rcGrade${value}`]}`}>
|
||||
{RC_LABELS[value]}
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
{parentView?.q_recommend_pct != null && parentView.total_responses != null && parentView.total_responses > 0 && (
|
||||
<p className={styles.parentRecommendLine}>
|
||||
<strong>{Math.round(parentView.q_recommend_pct)}%</strong> of parents would recommend this school ({parentView.total_responses.toLocaleString()} responses)
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
/* ── Old OEIF layout ── */
|
||||
<>
|
||||
<div className={styles.ofstedHeader}>
|
||||
<span className={`${styles.ofstedGrade} ${styles[`ofstedGrade${ofsted.overall_effectiveness}`]}`}>
|
||||
{ofsted.overall_effectiveness ? OFSTED_LABELS[ofsted.overall_effectiveness] : 'Not rated'}
|
||||
@@ -235,6 +287,8 @@ export function SchoolDetailView({
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
nextjs:
|
||||
build:
|
||||
|
||||
@@ -65,6 +65,10 @@ export interface School {
|
||||
// ============================================================================
|
||||
|
||||
export interface OfstedInspection {
|
||||
framework: 'OEIF' | 'ReportCard' | null;
|
||||
inspection_date: string | null;
|
||||
inspection_type: string | null;
|
||||
// OEIF fields (old framework, pre-Nov 2025)
|
||||
overall_effectiveness: 1 | 2 | 3 | 4 | null;
|
||||
quality_of_education: number | null;
|
||||
behaviour_attitudes: number | null;
|
||||
@@ -72,8 +76,17 @@ export interface OfstedInspection {
|
||||
leadership_management: number | null;
|
||||
early_years_provision: number | null;
|
||||
previous_overall: number | null;
|
||||
inspection_date: string | null;
|
||||
inspection_type: string | null;
|
||||
// Report Card fields (new framework, from Nov 2025)
|
||||
// 1=Exceptional 2=Strong 3=Expected standard 4=Needs attention 5=Urgent improvement
|
||||
rc_safeguarding_met: boolean | null;
|
||||
rc_inclusion: number | null;
|
||||
rc_curriculum_teaching: number | null;
|
||||
rc_achievement: number | null;
|
||||
rc_attendance_behaviour: number | null;
|
||||
rc_personal_development: number | null;
|
||||
rc_leadership_governance: number | null;
|
||||
rc_early_years: number | null;
|
||||
rc_sixth_form: number | null;
|
||||
}
|
||||
|
||||
export interface OfstedParentView {
|
||||
|
||||
Reference in New Issue
Block a user