feat(pipeline): dim_school/dim_location store GIAS codes; seed drift test

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Tudor
2026-07-09 10:45:15 +01:00
co-authored by Claude Fable 5
parent d898e6279b
commit fa6c929a3a
4 changed files with 78 additions and 32 deletions
+23 -27
View File
@@ -19,16 +19,17 @@ select
s.urn,
s.local_authority_code * 1000 + s.establishment_number as laestab,
s.school_name,
-- Phase in GIAS code space (see seeds/gias_code_names.csv):
-- 2 = Primary, 4 = Secondary, 7 = All-through, 0 = Not applicable.
case
-- 1. Trust GIAS phase when it's a real value (not the catch-all "Not Applicable")
when s.phase is not null
and lower(trim(s.phase)) not in ('not applicable', '', 'unknown')
then s.phase
-- 1. Trust GIAS phase when it's a real value (0 = the catch-all "Not Applicable")
when s.phase_code is not null and s.phase_code != 0
then s.phase_code
-- 2. Infer from statutory age range (independent schools still publish these)
when s.statutory_high_age is not null and s.statutory_high_age <= 11 then 'Primary'
when s.statutory_low_age is not null and s.statutory_low_age >= 11 then 'Secondary'
when s.statutory_high_age is not null and s.statutory_high_age <= 11 then 2
when s.statutory_low_age is not null and s.statutory_low_age >= 11 then 4
when s.statutory_low_age is not null and s.statutory_high_age is not null
and s.statutory_low_age < 11 and s.statutory_high_age > 11 then 'All-through'
and s.statutory_low_age < 11 and s.statutory_high_age > 11 then 7
-- 3. Fallback: infer from school name (covers independents with missing ages)
when s.school_name ilike '%primary%'
or s.school_name ilike '%infant%'
@@ -36,31 +37,27 @@ select
or s.school_name ilike '%preparatory%'
or s.school_name ilike '% prep school%'
or s.school_name ilike '% prep %'
then 'Primary'
then 2
when s.school_name ilike '%secondary%'
or s.school_name ilike '%high school%'
or s.school_name ilike '%grammar%'
or s.school_name ilike '%senior school%'
or s.school_name ilike '%upper school%'
then 'Secondary'
-- 4. Give up — leave phase null so the UI renders no pill
then 4
-- 4. Give up — null renders no phase pill
else null
end as phase,
s.school_type,
end as phase_code,
s.school_type_code,
s.academy_trust_name,
s.academy_trust_uid,
s.religious_character,
s.religious_character_code,
s.gender,
s.statutory_low_age || '-' || s.statutory_high_age as age_range,
-- Authoritative sixth-form flag (spec §3): GIAS OfficialSixthForm.
-- "Not applicable" (nurseries, primaries, PRUs) => false. Blank GIAS
-- value (rare, new establishments) falls back to the statutory age range.
-- lower(trim()) guards against casing/whitespace variants in raw GIAS
-- data, same as the phase derivation above — an unmatched variant would
-- otherwise silently fall through to the age-range fallback.
-- GIAS OfficialSixthForm in code space: 1 = has, 2 = does not, 0 = N/A.
-- Null (rare, new establishments) falls back to the statutory age range.
case
when lower(trim(s.official_sixth_form)) = 'has a sixth form' then true
when lower(trim(s.official_sixth_form)) in ('does not have a sixth form', 'not applicable') then false
when s.official_sixth_form_code = 1 then true
when s.official_sixth_form_code in (0, 2) then false
else coalesce(s.statutory_high_age >= 18, false)
end as has_sixth_form,
s.capacity,
@@ -70,9 +67,9 @@ select
s.telephone,
s.open_date,
s.close_date,
s.status,
s.status_code,
s.nursery_provision,
s.admissions_policy,
s.admissions_policy_code,
-- Latest Ofsted (populated after monthly Ofsted pipeline runs)
{% if ofsted_relation is not none %}
@@ -91,7 +88,6 @@ from schools s
{% if ofsted_relation is not none %}
left join {{ ref('int_ofsted_latest') }} o on s.urn = o.urn
{% endif %}
-- "Open, but proposed to close" schools are still operating (pupils enrolled,
-- results published)include them; they drop out automatically once GIAS
-- flips them to "Closed" (marts are fully rebuilt each run).
where s.status in ('Open', 'Open, but proposed to close')
-- 1 = Open; 3 = Open, but proposed to close (still operating; drops out when
-- GIAS flips to Closed — marts fully rebuild each run).
where s.status_code in (1, 3)