fix(backend): serve the current Ofsted status from fact_ofsted_latest
The list query and the batch Ofsted fetch each picked 'the latest row' of fact_ofsted_inspection themselves, and _ofsted_block carried an older ungraded grade forward when the latest graded inspection gave none. All three now read marts.fact_ofsted_latest. List rows: ofsted_grade is the grade still in force, ofsted_grade_date when it was awarded or confirmed, ofsted_date the latest visit. The ofsted block gains current_grade and latest_visit and loses grade_source; overall_effectiveness is the graded inspection's own result. A school with only an inspection stays publishable in the sitemap. Requires fact_ofsted_latest (PR #183's pipeline run) on the database. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
96732c56d1
commit
a9e3a6a700
8 files changed
+188
-78
No files matched your search
+37
-56
@@ -18,7 +18,7 @@ from .config import settings
|
||||
from .database import SessionLocal, engine
|
||||
from .models import (
|
||||
DimSchool, DimLocation, KS2Performance,
|
||||
FactOfstedInspection, FactAdmissions, FactAdmissionDistance,
|
||||
FactOfstedLatest, FactAdmissions, FactAdmissionDistance,
|
||||
FactDeprivation, FactFinance, FactPupilCharacteristics,
|
||||
FactKs4Destinations, FactKs5Destinations,
|
||||
)
|
||||
@@ -251,10 +251,11 @@ _MAIN_QUERY = text("""
|
||||
s.website,
|
||||
s.telephone,
|
||||
s.nursery_provision,
|
||||
foi.ofsted_grade,
|
||||
foi.ofsted_date,
|
||||
foi.ofsted_framework,
|
||||
foi.ofsted_rc_date,
|
||||
foi.current_grade AS ofsted_grade,
|
||||
foi.current_grade_date AS ofsted_grade_date,
|
||||
foi.latest_visit_date AS ofsted_date,
|
||||
foi.framework AS ofsted_framework,
|
||||
foi.rc_inspection_date AS ofsted_rc_date,
|
||||
l.local_authority_name AS local_authority,
|
||||
l.local_authority_code,
|
||||
l.address_line1 AS address1,
|
||||
@@ -335,21 +336,10 @@ _MAIN_QUERY = text("""
|
||||
FROM marts.dim_school s
|
||||
JOIN marts.dim_location l ON s.urn = l.urn
|
||||
LEFT JOIN marts.fact_performance p ON s.urn = p.urn
|
||||
LEFT JOIN (
|
||||
SELECT DISTINCT ON (urn)
|
||||
urn,
|
||||
-- Fall back to the ungraded-inspection grade when no graded grade exists.
|
||||
COALESCE(overall_effectiveness, ungraded_grade) AS ofsted_grade,
|
||||
inspection_date AS ofsted_date,
|
||||
framework AS ofsted_framework,
|
||||
-- Report-card signal for list/map badges: non-null only when the
|
||||
-- latest inspection carries report-card grades. framework is the
|
||||
-- raw event grouping ("Schools - S5"), never "ReportCard", so it
|
||||
-- can't be used to detect report cards.
|
||||
rc_inspection_date AS ofsted_rc_date
|
||||
FROM marts.fact_ofsted_inspection
|
||||
ORDER BY urn, inspection_date DESC NULLS LAST
|
||||
) foi ON s.urn = foi.urn
|
||||
-- One current Ofsted status per school (pipeline: int_ofsted_latest): the
|
||||
-- grade still in force, dated by the inspection that awarded or confirmed
|
||||
-- it, and the latest visit of any kind.
|
||||
LEFT JOIN marts.fact_ofsted_latest foi ON s.urn = foi.urn
|
||||
ORDER BY s.school_name, p.year
|
||||
""")
|
||||
|
||||
@@ -731,39 +721,36 @@ def compute_benchmarks(df: pd.DataFrame, census_benchmarks: dict | None = None)
|
||||
}
|
||||
|
||||
|
||||
def _iso(d):
|
||||
return d.isoformat() if d else None
|
||||
|
||||
|
||||
def _ofsted_block(o, urn: int) -> dict:
|
||||
"""Serialize the latest Ofsted inspection row for API responses.
|
||||
"""Serialize a fact_ofsted_latest row for API responses.
|
||||
|
||||
`grade_source` records where the effective overall grade came from:
|
||||
a graded (Section 5) inspection, or carried forward from an ungraded
|
||||
(Section 8) outcome — materially different claims a UI must be able
|
||||
to distinguish. `report_card` holds coded+labelled renewed-framework
|
||||
(Nov 2025) area judgements; safeguarding is a separate boolean and
|
||||
never appears among the graded areas.
|
||||
`current_grade` is the overall grade still in force, dated by the
|
||||
inspection that awarded or confirmed it; `latest_visit` is the school's
|
||||
most recent inspection of any kind. The rule lives in int_ofsted_latest
|
||||
(docs/superpowers/specs/2026-10-05-ofsted-current-status-design.md).
|
||||
`overall_effectiveness` and `inspection_date` describe the graded
|
||||
inspection itself and label its area judgements. `report_card` holds the
|
||||
renewed-framework (Nov 2025) area judgements; safeguarding is a separate
|
||||
boolean and never appears among the graded areas.
|
||||
"""
|
||||
if o.overall_effectiveness is not None:
|
||||
grade_source = "graded"
|
||||
overall = o.overall_effectiveness
|
||||
elif o.ungraded_grade is not None:
|
||||
# Fall back to the grade parsed from an ungraded (Section 8) outcome
|
||||
# (e.g. "School remains Good") so the detail page matches the list badge.
|
||||
grade_source = "ungraded_carried_forward"
|
||||
overall = o.ungraded_grade
|
||||
else:
|
||||
grade_source = None
|
||||
overall = None
|
||||
|
||||
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_date": _iso(o.graded_inspection_date),
|
||||
"rc_inspection_date": _iso(o.rc_inspection_date),
|
||||
"inspection_type": o.inspection_type,
|
||||
"overall_effectiveness": overall,
|
||||
"grade_source": grade_source,
|
||||
"overall_effectiveness": o.overall_effectiveness if o.overall_effectiveness in (1, 2, 3, 4) else None,
|
||||
"current_grade": (
|
||||
{"grade": o.current_grade, "date": _iso(o.current_grade_date), "basis": o.current_grade_basis}
|
||||
if o.current_grade is not None else None
|
||||
),
|
||||
"latest_visit": (
|
||||
{"date": _iso(o.latest_visit_date), "kind": o.latest_visit_kind, "outcome": o.latest_visit_outcome}
|
||||
if o.latest_visit_date else None
|
||||
),
|
||||
"quality_of_education": o.quality_of_education,
|
||||
"behaviour_attitudes": o.behaviour_attitudes,
|
||||
"personal_development": o.personal_development,
|
||||
@@ -1097,20 +1084,14 @@ def get_supplementary_data_batch(db: Session, urns: list[int]) -> dict:
|
||||
logging.getLogger(__name__).error("batch supplementary query failed: %s", e)
|
||||
db.rollback()
|
||||
|
||||
# Ofsted — latest inspection per URN. Ordered so the first row seen per
|
||||
# URN is the most recent.
|
||||
# Ofsted — the mart already holds one current status per URN.
|
||||
def _ofsted():
|
||||
rows = (
|
||||
db.query(FactOfstedInspection)
|
||||
.filter(FactOfstedInspection.urn.in_(urns))
|
||||
.order_by(FactOfstedInspection.urn, FactOfstedInspection.inspection_date.desc())
|
||||
db.query(FactOfstedLatest)
|
||||
.filter(FactOfstedLatest.urn.in_(urns))
|
||||
.all()
|
||||
)
|
||||
seen = set()
|
||||
for o in rows:
|
||||
if o.urn in seen:
|
||||
continue
|
||||
seen.add(o.urn)
|
||||
result[o.urn]["ofsted"] = _ofsted_block(o, o.urn)
|
||||
_safe(_ofsted)
|
||||
|
||||
|
||||
Reference in new issue
Block a user