feat(pipeline): extract Ofsted report-card judgements (rc_* columns)

Wires the tap TODO in stg_ofsted_inspections.sql: maps the 7 confirmed
report-card MI columns (Safeguarding standards, Inclusion, Curriculum
and teaching, Achievement, Attendance and behaviour, Personal
development and wellbeing, Leadership and governance) into rc_*
fields, parsed via the new parse_report_card_grade macro against
real sampled grade values (Exceptional/Strong standard/Expected
standard/Needs attention/Urgent improvement). rc_safeguarding_met
becomes boolean from Met/Not met. rc_early_years/rc_sixth_form have
no MI column yet and are intentionally omitted from COLUMN_PRIORITY,
staying NULL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
Tudor
2026-07-12 22:12:33 +01:00
co-authored by Claude Fable 5
parent bee63a7836
commit 02084e427c
3 changed files with 55 additions and 11 deletions
@@ -68,6 +68,19 @@ COLUMN_PRIORITY = {
"ungraded_inspection_date": [ "ungraded_inspection_date": [
"Date of latest ungraded inspection", "Date of latest ungraded inspection",
], ],
# Report Card fields (post-Nov 2025 framework). Confirmed verbatim MI
# headers per diagnose_compare_gaps.py's Task 1(c) findings. No MI column
# currently exists for early-years or sixth-form report-card grades, so
# those two fields are deliberately omitted here (see schema below) --
# they stay absent from every record, same as the existing `report_url`
# pattern for fields with no COLUMN_PRIORITY entry.
"rc_safeguarding_met": ["Safeguarding standards"],
"rc_inclusion": ["Inclusion"],
"rc_curriculum_teaching": ["Curriculum and teaching"],
"rc_achievement": ["Achievement"],
"rc_attendance_behaviour": ["Attendance and behaviour"],
"rc_personal_development": ["Personal development and wellbeing"],
"rc_leadership_governance": ["Leadership and governance"],
} }
@@ -111,6 +124,17 @@ class OfstedInspectionsStream(Stream):
th.Property("sixth_form_provision", th.StringType), th.Property("sixth_form_provision", th.StringType),
th.Property("ungraded_outcome", th.StringType), th.Property("ungraded_outcome", th.StringType),
th.Property("ungraded_inspection_date", th.StringType), th.Property("ungraded_inspection_date", th.StringType),
th.Property("rc_safeguarding_met", th.StringType),
th.Property("rc_inclusion", th.StringType),
th.Property("rc_curriculum_teaching", th.StringType),
th.Property("rc_achievement", th.StringType),
th.Property("rc_attendance_behaviour", th.StringType),
th.Property("rc_personal_development", th.StringType),
th.Property("rc_leadership_governance", th.StringType),
# No MI column exists for these yet; declared for forward
# compatibility with the mart schema, always emitted as absent/NULL.
th.Property("rc_early_years", th.StringType),
th.Property("rc_sixth_form", th.StringType),
th.Property("report_url", th.StringType), th.Property("report_url", th.StringType),
).to_dict() ).to_dict()
@@ -0,0 +1,17 @@
-- Macro: Parse Ofsted Report Card grade (post-Nov 2025 framework) from text
-- into the 5-point scale. Real values confirmed via a live sample of the MI
-- CSV (see pipeline/scripts/diagnose_compare_gaps.py's
-- "TASK 7 VALUE SAMPLE 2026-07-12" note) -- unrecognised text (including the
-- 'NULL' sentinel used by the source CSV for blanks) parses to NULL, never
-- errors.
{% macro parse_report_card_grade(column_name) %}
case lower(trim(nullif({{ column_name }}, 'NULL')))
when 'exceptional' then 1
when 'strong standard' then 2
when 'expected standard' then 3
when 'needs attention' then 4
when 'urgent improvement' then 5
else null
end
{% endmacro %}
@@ -33,17 +33,20 @@ renamed as (
nullif(trim(ungraded_outcome), 'NULL') as ungraded_outcome, nullif(trim(ungraded_outcome), 'NULL') as ungraded_outcome,
{{ parse_ungraded_outcome('ungraded_outcome') }}::integer as ungraded_grade, {{ parse_ungraded_outcome('ungraded_outcome') }}::integer as ungraded_grade,
-- Report Card fields (post-Nov 2025 framework) -- Report Card fields (post-Nov 2025 framework), 5-point scale:
-- TODO: add rc_* columns to tap-uk-ofsted schema once CSV column names are confirmed -- 1 Exceptional · 2 Strong standard · 3 Expected standard
null::text as rc_safeguarding_met, -- · 4 Needs attention · 5 Urgent improvement
null::text as rc_inclusion, (lower(trim(nullif(rc_safeguarding_met, 'NULL'))) = 'met') as rc_safeguarding_met,
null::text as rc_curriculum_teaching, {{ parse_report_card_grade('rc_inclusion') }}::integer as rc_inclusion,
null::text as rc_achievement, {{ parse_report_card_grade('rc_curriculum_teaching') }}::integer as rc_curriculum_teaching,
null::text as rc_attendance_behaviour, {{ parse_report_card_grade('rc_achievement') }}::integer as rc_achievement,
null::text as rc_personal_development, {{ parse_report_card_grade('rc_attendance_behaviour') }}::integer as rc_attendance_behaviour,
null::text as rc_leadership_governance, {{ parse_report_card_grade('rc_personal_development') }}::integer as rc_personal_development,
null::text as rc_early_years, {{ parse_report_card_grade('rc_leadership_governance') }}::integer as rc_leadership_governance,
null::text as rc_sixth_form, -- No MI column exists for these yet (see tap.py); the tap never
-- emits rc_early_years/rc_sixth_form, so these stay NULL.
null::integer as rc_early_years,
null::integer as rc_sixth_form,
report_url report_url
from source from source