From 61687b9d32842f7c2ca9d0640d1b51fdd40959d2 Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Mon, 8 Jun 2026 11:39:23 -0700 Subject: [PATCH 01/15] From PR#230: Add new bld disability models and stacked fact table+yaml --- .../bld_ef3__student__disabilities.sql | 36 ++++++++ ..._student__wide_disability_designations.sql | 45 ++++++++++ .../core_warehouse/fct_student_disability.sql | 72 ++++++++++++++++ .../core_warehouse/fct_student_disability.yml | 86 +++++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 models/build/edfi_3/students/bld_ef3__student__disabilities.sql create mode 100644 models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql create mode 100644 models/core_warehouse/fct_student_disability.sql create mode 100644 models/core_warehouse/fct_student_disability.yml diff --git a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql new file mode 100644 index 00000000..38a3e334 --- /dev/null +++ b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql @@ -0,0 +1,36 @@ +-- Define all optional disability models here. +{% set stage_disability_relations = [] %} + +--Ed Org Disabilities +{% do stage_disability_relations.append(ref('stg_ef3__stu_ed_org__disabilities')) %} + +-- Special Education +{% if var('src:program:special_ed:enabled', True) %} + {% do stage_disability_relations.append(ref('stg_ef3__stu_spec_ed__disabilities')) %} +{% endif %} + +with stacked as ( + {{ dbt_utils.union_relations( + relations=stage_disability_relations + ) }} +), +formatted as ( + select + tenant_code, + api_year, + school_year, + k_student, + ed_org_id, + k_lea, + k_school, + k_program, + program_enroll_begin_date, + program_enroll_end_date, + disability_type, + disability_source_type, + disability_diagnosis, + order_of_disability, + v_designations + from stacked +) +select * from formatted diff --git a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql new file mode 100644 index 00000000..112c6d96 --- /dev/null +++ b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql @@ -0,0 +1,45 @@ +with student_disabilities as ( + select * from {{ ref('bld_ef3__student__disabilities') }} +), +xwalk_disability_designations as ( + select * from {{ ref('xwalk_disability_designations') }} +), +flattened as ( + select + tenant_code, + api_year, + school_year, + k_student, + ed_org_id, + k_lea, + k_school, + k_program, + disability_type, + {{ edu_edfi_source.extract_descriptor('designation.value:disabilityDesignationDescriptor::string') }} as disability_designation + from student_disabilities + {{ edu_edfi_source.json_flatten('v_designations', 'designation', outer=true) }} +), +pivoted as ( + select + tenant_code, + api_year, + school_year, + k_student, + ed_org_id, + k_lea, + k_school, + k_program, + disability_type + {%- if not is_empty_model('xwalk_disability_designations') -%}, + {{ ea_pivot( + column='indicator_name', + values=dbt_utils.get_column_values(ref('xwalk_disability_designations'), 'indicator_name'), + cast='boolean', + ) }} + {%- endif %} + from flattened + left outer join xwalk_disability_designations + on flattened.disability_designation = xwalk_disability_designations.disability_designation_descriptor + group by all +) +select * from pivoted diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql new file mode 100644 index 00000000..d1b4de34 --- /dev/null +++ b/models/core_warehouse/fct_student_disability.sql @@ -0,0 +1,72 @@ +{{ + config( + post_hook=[ + "alter table {{ this }} alter column k_student_disability set not null", + "alter table {{ this }} alter column k_student set not null", + "alter table {{ this }} alter column disability_type set not null", + "alter table {{ this }} add primary key (k_student_disability)", + "alter table {{ this }} add constraint fk_{{ this.name }}_student foreign key (k_student) references {{ ref('dim_student') }}", + "alter table {{ this }} add constraint fk_{{ this.name }}_program foreign key (k_program) references {{ ref('dim_program') }}", + "alter table {{ this }} add constraint fk_{{ this.name }}_lea foreign key (k_lea) references {{ ref('dim_lea') }}", + "alter table {{ this }} add constraint fk_{{ this.name }}_school foreign key (k_school) references {{ ref('dim_school') }}", + ] + ) +}} + +with student_disabilities as ( + select * from {{ ref('bld_ef3__student__disabilities') }} +), +dim_student as ( + select * from {{ ref('dim_student') }} +), +student_disability_designations as ( + select * from {{ ref('bld_ef3__student__wide_disability_designations') }} +), +formatted as ( + select + {{ dbt_utils.generate_surrogate_key( + ['sd.tenant_code', + 'sd.school_year', + 'sd.k_student', + 'sd.k_lea', + 'sd.k_school', + 'sd.k_program', + 'sd.ed_org_id', + 'sd.program_enroll_begin_date',] + ) }} as k_student_disability, + sd.k_student, + stu.k_student_xyear, + sd.k_lea, + sd.k_school, + sd.k_program, + sd.ed_org_id, + sd.program_enroll_begin_date, + sd.program_enroll_end_date, + sd.tenant_code, + sd.api_year, + sd.school_year, + sd.disability_type, + sd.disability_source_type, + sd.disability_diagnosis, + sd.order_of_disability, + -- disability designations + {{ accordion_columns( + source_table='bld_ef3__student__wide_disability_designations', + exclude_columns=['tenant_code', 'api_year', 'school_year', 'k_student', 'ed_org_id', 'k_lea', 'k_school', 'k_program', 'disability_type'], + source_alias='disability_designations', + add_trailing_comma=false + ) }} + from student_disabilities sd + join dim_student stu + on stu.k_student = sd.k_student + left join student_disability_designations disability_designations + on sd.k_student = disability_designations.k_student + and (sd.k_lea = disability_designations.k_lea or (sd.k_lea is null and disability_designations.k_lea is null)) + and (sd.k_school = disability_designations.k_school or (sd.k_school is null and disability_designations.k_school is null)) + and (sd.k_program = disability_designations.k_program or (sd.k_program is null and disability_designations.k_program is null)) + and sd.ed_org_id = disability_designations.ed_org_id + and sd.tenant_code = disability_designations.tenant_code + and sd.school_year = disability_designations.school_year + and sd.disability_type = disability_designations.disability_type +) +select * from formatted diff --git a/models/core_warehouse/fct_student_disability.yml b/models/core_warehouse/fct_student_disability.yml new file mode 100644 index 00000000..0a8436b2 --- /dev/null +++ b/models/core_warehouse/fct_student_disability.yml @@ -0,0 +1,86 @@ +version: 3 + +models: + - name: fct_student_disability + description: > + ##### Overview: + This fact table provides student disabilities, which could be assigned from multiple sources. + It references any or all of the following models: + - [stg_ef3__stu_ed_org__disabilities](#!/model/model.edu_edfi_source.stg_ef3__stu_ed_org__disabilities) + - [stg_ef3__stu_spec_ed__disabilities](#!/model/model.edu_edfi_source.stg_ef3__stu_spec_ed__disabilities) + + ##### Primary Key: + `k_student_disability` -- + There is one record per student, year, ed org / program + + ##### Important business rules: + - This table is at two different grains since both Ed Orgs and Special Ed Programs can have disabilities listed. This is why this table has + a surrogate key being made up of the two grains that fit in this table. If k_program is null then this is an ed org disability. + If k_program is not null then it is a program disability. + - `program_enroll_begin_date` is included in the unique key, because a student may be associated with the same program at multiple times, + and those associations may have different disabilities. When joining this table to `fct_student_{program_type}_program_association`, + always include `program_enroll_begin_date` in the join (see example query below). + + ##### Example Use Cases: + 1. Join Disabilities to Student Special Education data to find students' Special Ed disabilities + they received as part of that program: + + ``` + SELECT + assoc.k_student, + assoc.school_year, + assoc.k_program, + dim_program.program_type, + dim_program.program_id, + dim_program.program_name, + assoc.program_enroll_begin_date, + assoc.program_enroll_end_date, + dis.disability_type, + dis.disability_source_type, + dis.disability_diagnosis, + dis.order_of_disability + FROM analytics.prod_wh.fct_student_special_education_program_association assoc + JOIN analytics.prod_wh.dim_program + ON assoc.k_program = dim_program.k_program + -- left join because some programs may not have disabilities + LEFT join analytics.prod_wh.fct_student_disability dis + ON assoc.k_student = dis.k_student + AND assoc.k_program = dis.k_program + AND assoc.program_enroll_begin_date = dis.program_enroll_begin_date; + ``` + + + config: + tags: ['special_ed'] + enabled: > + {%- set var_values = [] -%} + {%- for variable in [ + 'src:program:special_ed:enabled', + ] -%} + {%- do var_values.append(var(variable, none)) -%} + {%- endfor -%} + + {%- if True in var_values -%} + {{ True | as_bool }} + {%- elif False in var_values -%} + {{ False | as_bool }} + {%- else -%} + {{ True | as_bool }} + {%- endif -%} + + columns: + - name: k_student_disability, + - name: k_student + - name: k_student_xyear + - name: k_lea + - name: k_school + - name: k_program + - name: program_enroll_begin_date + - name: program_enroll_end_date + - name: tenant_code + - name: api_year + - name: school_year + - name: disability_type + - name: disability_source_type + - name: disability_diagnosis + - name: order_of_disability From da95da6db5df2b5a78cb3b9ea4c33b71d2c62187 Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 10:49:46 -0700 Subject: [PATCH 02/15] removed edorg id which can be inferred through k_lea and k_school --- .../build/edfi_3/students/bld_ef3__student__disabilities.sql | 1 - .../bld_ef3__student__wide_disability_designations.sql | 2 -- models/core_warehouse/fct_student_disability.sql | 5 +---- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql index 38a3e334..1275c098 100644 --- a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql +++ b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql @@ -20,7 +20,6 @@ formatted as ( api_year, school_year, k_student, - ed_org_id, k_lea, k_school, k_program, diff --git a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql index 112c6d96..9442e075 100644 --- a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql +++ b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql @@ -10,7 +10,6 @@ flattened as ( api_year, school_year, k_student, - ed_org_id, k_lea, k_school, k_program, @@ -25,7 +24,6 @@ pivoted as ( api_year, school_year, k_student, - ed_org_id, k_lea, k_school, k_program, diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index d1b4de34..0e156ffd 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -31,7 +31,6 @@ formatted as ( 'sd.k_lea', 'sd.k_school', 'sd.k_program', - 'sd.ed_org_id', 'sd.program_enroll_begin_date',] ) }} as k_student_disability, sd.k_student, @@ -39,7 +38,6 @@ formatted as ( sd.k_lea, sd.k_school, sd.k_program, - sd.ed_org_id, sd.program_enroll_begin_date, sd.program_enroll_end_date, sd.tenant_code, @@ -52,7 +50,7 @@ formatted as ( -- disability designations {{ accordion_columns( source_table='bld_ef3__student__wide_disability_designations', - exclude_columns=['tenant_code', 'api_year', 'school_year', 'k_student', 'ed_org_id', 'k_lea', 'k_school', 'k_program', 'disability_type'], + exclude_columns=['tenant_code', 'api_year', 'school_year', 'k_student', 'k_lea', 'k_school', 'k_program', 'disability_type'], source_alias='disability_designations', add_trailing_comma=false ) }} @@ -64,7 +62,6 @@ formatted as ( and (sd.k_lea = disability_designations.k_lea or (sd.k_lea is null and disability_designations.k_lea is null)) and (sd.k_school = disability_designations.k_school or (sd.k_school is null and disability_designations.k_school is null)) and (sd.k_program = disability_designations.k_program or (sd.k_program is null and disability_designations.k_program is null)) - and sd.ed_org_id = disability_designations.ed_org_id and sd.tenant_code = disability_designations.tenant_code and sd.school_year = disability_designations.school_year and sd.disability_type = disability_designations.disability_type From c54c4bfed03ef7eb4498fb27003b67f2d7d7c0bb Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 10:58:08 -0700 Subject: [PATCH 03/15] added k_student_program and is_program flag so we can identify which rows are edorg level and which are program level --- .../build/edfi_3/students/bld_ef3__student__disabilities.sql | 5 ++++- models/core_warehouse/fct_student_disability.sql | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql index 1275c098..ea9efb6e 100644 --- a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql +++ b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql @@ -23,13 +23,16 @@ formatted as ( k_lea, k_school, k_program, + k_student_program, program_enroll_begin_date, program_enroll_end_date, disability_type, disability_source_type, disability_diagnosis, order_of_disability, - v_designations + v_designations, + -- if k_program is populated, this is a program disability; otherwise it is an ed org disability. + k_program is not null as is_program from stacked ) select * from formatted diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index 0e156ffd..6bc2e546 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -38,6 +38,8 @@ formatted as ( sd.k_lea, sd.k_school, sd.k_program, + sd.k_student_program, + sd.is_program, sd.program_enroll_begin_date, sd.program_enroll_end_date, sd.tenant_code, From b7b3531ea555aa70217bf215b22f0348ed158daa Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 10:58:29 -0700 Subject: [PATCH 04/15] add disability type to k_student_disability key --- models/core_warehouse/fct_student_disability.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index 6bc2e546..f984cdee 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -31,7 +31,8 @@ formatted as ( 'sd.k_lea', 'sd.k_school', 'sd.k_program', - 'sd.program_enroll_begin_date',] + 'sd.program_enroll_begin_date', + 'sd.disability_type',] ) }} as k_student_disability, sd.k_student, stu.k_student_xyear, From 449174a37b459c3936fc5e443c15d6fd9fbd0c1f Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 11:00:20 -0700 Subject: [PATCH 05/15] update alias in fct_student_disability to follow standard of repo, which is to usually use the ref name directly --- .../core_warehouse/fct_student_disability.sql | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index f984cdee..fd96fe2e 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -25,31 +25,31 @@ student_disability_designations as ( formatted as ( select {{ dbt_utils.generate_surrogate_key( - ['sd.tenant_code', - 'sd.school_year', - 'sd.k_student', - 'sd.k_lea', - 'sd.k_school', - 'sd.k_program', - 'sd.program_enroll_begin_date', - 'sd.disability_type',] + ['student_disabilities.tenant_code', + 'student_disabilities.school_year', + 'student_disabilities.k_student', + 'student_disabilities.k_lea', + 'student_disabilities.k_school', + 'student_disabilities.k_program', + 'student_disabilities.program_enroll_begin_date', + 'student_disabilities.disability_type',] ) }} as k_student_disability, - sd.k_student, - stu.k_student_xyear, - sd.k_lea, - sd.k_school, - sd.k_program, - sd.k_student_program, - sd.is_program, - sd.program_enroll_begin_date, - sd.program_enroll_end_date, - sd.tenant_code, - sd.api_year, - sd.school_year, - sd.disability_type, - sd.disability_source_type, - sd.disability_diagnosis, - sd.order_of_disability, + student_disabilities.k_student, + dim_student.k_student_xyear, + student_disabilities.k_lea, + student_disabilities.k_school, + student_disabilities.k_program, + student_disabilities.k_student_program, + student_disabilities.is_program, + student_disabilities.program_enroll_begin_date, + student_disabilities.program_enroll_end_date, + student_disabilities.tenant_code, + student_disabilities.api_year, + student_disabilities.school_year, + student_disabilities.disability_type, + student_disabilities.disability_source_type, + student_disabilities.disability_diagnosis, + student_disabilities.order_of_disability, -- disability designations {{ accordion_columns( source_table='bld_ef3__student__wide_disability_designations', @@ -57,16 +57,16 @@ formatted as ( source_alias='disability_designations', add_trailing_comma=false ) }} - from student_disabilities sd - join dim_student stu - on stu.k_student = sd.k_student - left join student_disability_designations disability_designations - on sd.k_student = disability_designations.k_student - and (sd.k_lea = disability_designations.k_lea or (sd.k_lea is null and disability_designations.k_lea is null)) - and (sd.k_school = disability_designations.k_school or (sd.k_school is null and disability_designations.k_school is null)) - and (sd.k_program = disability_designations.k_program or (sd.k_program is null and disability_designations.k_program is null)) - and sd.tenant_code = disability_designations.tenant_code - and sd.school_year = disability_designations.school_year - and sd.disability_type = disability_designations.disability_type + from student_disabilities + join dim_student + on dim_student.k_student = student_disabilities.k_student + left join student_disability_designations + on student_disabilities.k_student = student_disability_designations.k_student + and (student_disabilities.k_lea = student_disability_designations.k_lea or (student_disabilities.k_lea is null and student_disability_designations.k_lea is null)) + and (student_disabilities.k_school = student_disability_designations.k_school or (student_disabilities.k_school is null and student_disability_designations.k_school is null)) + and (student_disabilities.k_program = student_disability_designations.k_program or (student_disabilities.k_program is null and student_disability_designations.k_program is null)) + and student_disabilities.tenant_code = student_disability_designations.tenant_code + and student_disabilities.school_year = student_disability_designations.school_year + and student_disabilities.disability_type = student_disability_designations.disability_type ) select * from formatted From 3080df150ed54e33eed8ef711ce64dc81febacf5 Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 11:04:52 -0700 Subject: [PATCH 06/15] Other formatting changes --- .../build/edfi_3/students/bld_ef3__student__disabilities.sql | 2 +- models/core_warehouse/fct_student_disability.sql | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql index ea9efb6e..eac2bfbf 100644 --- a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql +++ b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql @@ -1,7 +1,7 @@ -- Define all optional disability models here. {% set stage_disability_relations = [] %} ---Ed Org Disabilities +-- Ed Org Disabilities {% do stage_disability_relations.append(ref('stg_ef3__stu_ed_org__disabilities')) %} -- Special Education diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index fd96fe2e..e16f9e03 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -50,7 +50,7 @@ formatted as ( student_disabilities.disability_source_type, student_disabilities.disability_diagnosis, student_disabilities.order_of_disability, - -- disability designations + -- Pivot disability designation descriptors into boolean columns. {{ accordion_columns( source_table='bld_ef3__student__wide_disability_designations', exclude_columns=['tenant_code', 'api_year', 'school_year', 'k_student', 'k_lea', 'k_school', 'k_program', 'disability_type'], @@ -58,7 +58,7 @@ formatted as ( add_trailing_comma=false ) }} from student_disabilities - join dim_student + inner join dim_student on dim_student.k_student = student_disabilities.k_student left join student_disability_designations on student_disabilities.k_student = student_disability_designations.k_student From 872b7f0735887a8bdbe9e6c2a0a3417f809b55bd Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 11:12:50 -0700 Subject: [PATCH 07/15] add/update documentation for fact and build models --- .../edfi_3/students/_edfi_3__students.yml | 8 +++- .../core_warehouse/fct_student_disability.yml | 40 +++++-------------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/models/build/edfi_3/students/_edfi_3__students.yml b/models/build/edfi_3/students/_edfi_3__students.yml index ec10948b..70d5f13c 100644 --- a/models/build/edfi_3/students/_edfi_3__students.yml +++ b/models/build/edfi_3/students/_edfi_3__students.yml @@ -84,4 +84,10 @@ models: - name: bld_ef3__student_program__migrant_education config: tags: ['migrant_education'] - enabled: "{{ var('src:program:migrant_education:enabled', True) }}" \ No newline at end of file + enabled: "{{ var('src:program:migrant_education:enabled', True) }}" + - name: bld_ef3__student__disabilities + config: + tags: ['special_ed'] + - name: bld_ef3__student__wide_disability_designations + config: + tags: ['special_ed'] \ No newline at end of file diff --git a/models/core_warehouse/fct_student_disability.yml b/models/core_warehouse/fct_student_disability.yml index 0a8436b2..1e9d6a95 100644 --- a/models/core_warehouse/fct_student_disability.yml +++ b/models/core_warehouse/fct_student_disability.yml @@ -1,4 +1,4 @@ -version: 3 +version: 2 models: - name: fct_student_disability @@ -10,16 +10,13 @@ models: - [stg_ef3__stu_spec_ed__disabilities](#!/model/model.edu_edfi_source.stg_ef3__stu_spec_ed__disabilities) ##### Primary Key: - `k_student_disability` -- - There is one record per student, year, ed org / program + `k_student_disability`: one record per student, year, ed org or program enrollment, and disability type. ##### Important business rules: - - This table is at two different grains since both Ed Orgs and Special Ed Programs can have disabilities listed. This is why this table has - a surrogate key being made up of the two grains that fit in this table. If k_program is null then this is an ed org disability. - If k_program is not null then it is a program disability. - - `program_enroll_begin_date` is included in the unique key, because a student may be associated with the same program at multiple times, - and those associations may have different disabilities. When joining this table to `fct_student_{program_type}_program_association`, - always include `program_enroll_begin_date` in the join (see example query below). + - This table is at two different grains since both Ed Orgs and Special Ed Programs can have disabilities listed. + Use `is_program` to distinguish: `false` means an ed org disability, `true` means a program disability. + - `program_enroll_begin_date`, `k_program` and `k_student_program` are null for ed org rows. When joining to + `fct_student_{program_type}_program_association`, join on `k_student_program` (see example query below). ##### Example Use Cases: 1. Join Disabilities to Student Special Education data to find students' Special Ed disabilities @@ -43,38 +40,23 @@ models: JOIN analytics.prod_wh.dim_program ON assoc.k_program = dim_program.k_program -- left join because some programs may not have disabilities - LEFT join analytics.prod_wh.fct_student_disability dis - ON assoc.k_student = dis.k_student - AND assoc.k_program = dis.k_program - AND assoc.program_enroll_begin_date = dis.program_enroll_begin_date; + LEFT JOIN analytics.prod_wh.fct_student_disability dis + ON assoc.k_student_program = dis.k_student_program; ``` config: tags: ['special_ed'] - enabled: > - {%- set var_values = [] -%} - {%- for variable in [ - 'src:program:special_ed:enabled', - ] -%} - {%- do var_values.append(var(variable, none)) -%} - {%- endfor -%} - - {%- if True in var_values -%} - {{ True | as_bool }} - {%- elif False in var_values -%} - {{ False | as_bool }} - {%- else -%} - {{ True | as_bool }} - {%- endif -%} columns: - - name: k_student_disability, + - name: k_student_disability - name: k_student - name: k_student_xyear - name: k_lea - name: k_school - name: k_program + - name: k_student_program + - name: is_program - name: program_enroll_begin_date - name: program_enroll_end_date - name: tenant_code From f1c0fb8d319cd9ec27f6e9652ccc69df73f4f748 Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 13:00:06 -0700 Subject: [PATCH 08/15] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec85d368..21d213d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased ## New features +- Added `fct_student_disability`, a new fact table unifying student disabilities from ed org associations and special education program associations into a single model. Includes `is_program` to distinguish the two grains, `k_student_program` for joining to program association fact tables, and extensible boolean designation columns via `xwalk_disability_designations`. +- Breaking change: New xwalk is required, `xwalk_disability_designations`, to pivot disability designation descriptors into boolean indicator columns in `fct_student_disability`. Expected columns: `disability_designation_descriptor` (the Ed-Fi descriptor value to match) and `indicator_name` (the name of the resulting boolean column). If the seed is empty, the designation columns are omitted and the model builds without them. ## Under the hood ## Fixes From 4c1b67a97323a604b357846c684fd0e9ec7775b3 Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 13:23:03 -0700 Subject: [PATCH 09/15] update accordion_column bug introduced by my earlier alias rename change --- models/core_warehouse/fct_student_disability.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index e16f9e03..38af8b60 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -54,7 +54,7 @@ formatted as ( {{ accordion_columns( source_table='bld_ef3__student__wide_disability_designations', exclude_columns=['tenant_code', 'api_year', 'school_year', 'k_student', 'k_lea', 'k_school', 'k_program', 'disability_type'], - source_alias='disability_designations', + source_alias='student_disability_designations', add_trailing_comma=false ) }} from student_disabilities From 43ef26a8c44529cc69d7f94837b6d32d2b9f3669 Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 13:37:28 -0700 Subject: [PATCH 10/15] Move k_student_disability join upstream to ease heavy join in fct_student_disability --- .../bld_ef3__student__disabilities.sql | 12 +++++++++++ ..._student__wide_disability_designations.sql | 6 ++++++ .../core_warehouse/fct_student_disability.sql | 21 +++---------------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql index eac2bfbf..cb3b8815 100644 --- a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql +++ b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql @@ -16,6 +16,18 @@ with stacked as ( ), formatted as ( select + -- Generated here so bld_ef3__student__wide_disability_designations can carry it forward, + -- allowing fct_student_disability to join on a single key rather than a null-safe multi-column join. + {{ dbt_utils.generate_surrogate_key([ + 'tenant_code', + 'school_year', + 'k_student', + 'k_lea', + 'k_school', + 'k_program', + 'program_enroll_begin_date', + 'disability_type', + ]) }} as k_student_disability, tenant_code, api_year, school_year, diff --git a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql index 9442e075..2a6e73f4 100644 --- a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql +++ b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql @@ -6,6 +6,7 @@ xwalk_disability_designations as ( ), flattened as ( select + k_student_disability, tenant_code, api_year, school_year, @@ -13,6 +14,8 @@ flattened as ( k_lea, k_school, k_program, + k_student_program, + is_program, disability_type, {{ edu_edfi_source.extract_descriptor('designation.value:disabilityDesignationDescriptor::string') }} as disability_designation from student_disabilities @@ -20,6 +23,7 @@ flattened as ( ), pivoted as ( select + k_student_disability, tenant_code, api_year, school_year, @@ -27,6 +31,8 @@ pivoted as ( k_lea, k_school, k_program, + k_student_program, + is_program, disability_type {%- if not is_empty_model('xwalk_disability_designations') -%}, {{ ea_pivot( diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index 38af8b60..2577b25e 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -24,16 +24,7 @@ student_disability_designations as ( ), formatted as ( select - {{ dbt_utils.generate_surrogate_key( - ['student_disabilities.tenant_code', - 'student_disabilities.school_year', - 'student_disabilities.k_student', - 'student_disabilities.k_lea', - 'student_disabilities.k_school', - 'student_disabilities.k_program', - 'student_disabilities.program_enroll_begin_date', - 'student_disabilities.disability_type',] - ) }} as k_student_disability, + student_disabilities.k_student_disability, student_disabilities.k_student, dim_student.k_student_xyear, student_disabilities.k_lea, @@ -53,7 +44,7 @@ formatted as ( -- Pivot disability designation descriptors into boolean columns. {{ accordion_columns( source_table='bld_ef3__student__wide_disability_designations', - exclude_columns=['tenant_code', 'api_year', 'school_year', 'k_student', 'k_lea', 'k_school', 'k_program', 'disability_type'], + exclude_columns=['k_student_disability', 'tenant_code', 'api_year', 'school_year', 'k_student', 'k_lea', 'k_school', 'k_program', 'k_student_program', 'is_program', 'disability_type'], source_alias='student_disability_designations', add_trailing_comma=false ) }} @@ -61,12 +52,6 @@ formatted as ( inner join dim_student on dim_student.k_student = student_disabilities.k_student left join student_disability_designations - on student_disabilities.k_student = student_disability_designations.k_student - and (student_disabilities.k_lea = student_disability_designations.k_lea or (student_disabilities.k_lea is null and student_disability_designations.k_lea is null)) - and (student_disabilities.k_school = student_disability_designations.k_school or (student_disabilities.k_school is null and student_disability_designations.k_school is null)) - and (student_disabilities.k_program = student_disability_designations.k_program or (student_disabilities.k_program is null and student_disability_designations.k_program is null)) - and student_disabilities.tenant_code = student_disability_designations.tenant_code - and student_disabilities.school_year = student_disability_designations.school_year - and student_disabilities.disability_type = student_disability_designations.disability_type + on student_disabilities.k_student_disability = student_disability_designations.k_student_disability ) select * from formatted From 82d1da90d1765486c23bbb28a0a8eadb41197e0f Mon Sep 17 00:00:00 2001 From: Angelica Lastra Date: Tue, 9 Jun 2026 14:07:06 -0700 Subject: [PATCH 11/15] fix join formatting --- .../students/bld_ef3__student__wide_disability_designations.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql index 2a6e73f4..387df1c7 100644 --- a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql +++ b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql @@ -42,7 +42,7 @@ pivoted as ( ) }} {%- endif %} from flattened - left outer join xwalk_disability_designations + left join xwalk_disability_designations on flattened.disability_designation = xwalk_disability_designations.disability_designation_descriptor group by all ) From e67c41799c2e277c023167c540accac5ae07afde Mon Sep 17 00:00:00 2001 From: rlittle08 Date: Mon, 27 Jul 2026 16:44:22 -0500 Subject: [PATCH 12/15] remove api_year: it's unuseful and empty for spec_ed disabs --- models/build/edfi_3/students/bld_ef3__student__disabilities.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql index cb3b8815..6b9a9dab 100644 --- a/models/build/edfi_3/students/bld_ef3__student__disabilities.sql +++ b/models/build/edfi_3/students/bld_ef3__student__disabilities.sql @@ -29,7 +29,6 @@ formatted as ( 'disability_type', ]) }} as k_student_disability, tenant_code, - api_year, school_year, k_student, k_lea, From 3675fcbe372ccff319095c5f25be32697212003a Mon Sep 17 00:00:00 2001 From: rlittle08 Date: Mon, 27 Jul 2026 16:57:03 -0500 Subject: [PATCH 13/15] remove faulty constraints (optional k_cols), rearrange and doc cols --- .../core_warehouse/fct_student_disability.sql | 9 ++-- .../core_warehouse/fct_student_disability.yml | 43 +++++++++++++++++-- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index 2577b25e..7f16e4e3 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -7,8 +7,6 @@ "alter table {{ this }} add primary key (k_student_disability)", "alter table {{ this }} add constraint fk_{{ this.name }}_student foreign key (k_student) references {{ ref('dim_student') }}", "alter table {{ this }} add constraint fk_{{ this.name }}_program foreign key (k_program) references {{ ref('dim_program') }}", - "alter table {{ this }} add constraint fk_{{ this.name }}_lea foreign key (k_lea) references {{ ref('dim_lea') }}", - "alter table {{ this }} add constraint fk_{{ this.name }}_school foreign key (k_school) references {{ ref('dim_school') }}", ] ) }} @@ -31,12 +29,11 @@ formatted as ( student_disabilities.k_school, student_disabilities.k_program, student_disabilities.k_student_program, + student_disabilities.tenant_code, + student_disabilities.school_year, student_disabilities.is_program, student_disabilities.program_enroll_begin_date, student_disabilities.program_enroll_end_date, - student_disabilities.tenant_code, - student_disabilities.api_year, - student_disabilities.school_year, student_disabilities.disability_type, student_disabilities.disability_source_type, student_disabilities.disability_diagnosis, @@ -49,7 +46,7 @@ formatted as ( add_trailing_comma=false ) }} from student_disabilities - inner join dim_student + join dim_student on dim_student.k_student = student_disabilities.k_student left join student_disability_designations on student_disabilities.k_student_disability = student_disability_designations.k_student_disability diff --git a/models/core_warehouse/fct_student_disability.yml b/models/core_warehouse/fct_student_disability.yml index 1e9d6a95..40b4021c 100644 --- a/models/core_warehouse/fct_student_disability.yml +++ b/models/core_warehouse/fct_student_disability.yml @@ -48,21 +48,58 @@ models: config: tags: ['special_ed'] + constraints: + - type: primary_key + columns: [k_student_disability] + - type: foreign_key + name: fk__fct_student_disability__k_student + columns: [k_student] + to: ref('dim_student') + to_columns: [k_student] + - type: foreign_key + name: fk__fct_student_disability__k_program + columns: [k_program] + to: ref('dim_program') + to_columns: [k_program] + columns: - name: k_student_disability + description: > + Surrogate primary key. Generated from `tenant_code`, `school_year`, `k_student`, `k_lea`, + `k_school`, `k_program`, `program_enroll_begin_date`, and `disability_type`. - name: k_student + description: Unique identifier for the student-year. Foreign key reference to [dim_student](#!/model/model.edu_wh.dim_student). - name: k_student_xyear + description: Defining key for the student, which is consistent across years. - name: k_lea + description: Unique key identifying the Local Education Agency (LEA) associated with this disability record. - name: k_school + description: Unique key identifying the school associated with this disability record. - name: k_program + description: > + Unique identifier for the program associated with this disability. Foreign key reference to + [dim_program](#!/model/model.edu_wh.dim_program). Null for ed org disability rows. - name: k_student_program + description: > + Surrogate key for the student's program enrollment. Use this to join to + `fct_student_{program_type}_program_association` tables. Null for ed org disability rows. + - name: tenant_code + description: The tenant associated with the record. + - name: school_year + description: > + School year specified by Spring year. + e.g. the 2021-2022 year would be 2022. - name: is_program + description: Whether the disability is sourced from a program association (true) or an ed org association (false). - name: program_enroll_begin_date + description: The date the student began enrollment in the program associated with this disability. Null for ed org disability rows. - name: program_enroll_end_date - - name: tenant_code - - name: api_year - - name: school_year + description: The date the student's enrollment in the associated program ended, if applicable. Null for ed org disability rows. - name: disability_type + description: A disability category that describes the student's impairment (e.g. Autism, Intellectual Disability). - name: disability_source_type + description: The source that provided the disability determination (e.g. Medical, Self). - name: disability_diagnosis + description: A more specific description of the disability diagnosis, if provided. - name: order_of_disability + description: The ordinal ranking when a student has multiple disabilities listed, where 1 indicates the primary disability. From bee773b5fd73c051c7ef13abb2835a0837098b30 Mon Sep 17 00:00:00 2001 From: rlittle08 Date: Thu, 30 Jul 2026 08:06:35 -0500 Subject: [PATCH 14/15] if k_program is null, this constraint will also fail --- models/core_warehouse/fct_student_disability.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index 7f16e4e3..6d6cfb9c 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -6,7 +6,6 @@ "alter table {{ this }} alter column disability_type set not null", "alter table {{ this }} add primary key (k_student_disability)", "alter table {{ this }} add constraint fk_{{ this.name }}_student foreign key (k_student) references {{ ref('dim_student') }}", - "alter table {{ this }} add constraint fk_{{ this.name }}_program foreign key (k_program) references {{ ref('dim_program') }}", ] ) }} From b56f770e9da98b93f8a62bce44df0ac1ca06e409 Mon Sep 17 00:00:00 2001 From: rlittle08 Date: Thu, 30 Jul 2026 08:55:27 -0500 Subject: [PATCH 15/15] re-add program fkey (actually does work on nulls); fix for api_year removal --- .../students/bld_ef3__student__wide_disability_designations.sql | 2 -- models/core_warehouse/fct_student_disability.sql | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql index 387df1c7..f0919017 100644 --- a/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql +++ b/models/build/edfi_3/students/bld_ef3__student__wide_disability_designations.sql @@ -8,7 +8,6 @@ flattened as ( select k_student_disability, tenant_code, - api_year, school_year, k_student, k_lea, @@ -25,7 +24,6 @@ pivoted as ( select k_student_disability, tenant_code, - api_year, school_year, k_student, k_lea, diff --git a/models/core_warehouse/fct_student_disability.sql b/models/core_warehouse/fct_student_disability.sql index 6d6cfb9c..7f16e4e3 100644 --- a/models/core_warehouse/fct_student_disability.sql +++ b/models/core_warehouse/fct_student_disability.sql @@ -6,6 +6,7 @@ "alter table {{ this }} alter column disability_type set not null", "alter table {{ this }} add primary key (k_student_disability)", "alter table {{ this }} add constraint fk_{{ this.name }}_student foreign key (k_student) references {{ ref('dim_student') }}", + "alter table {{ this }} add constraint fk_{{ this.name }}_program foreign key (k_program) references {{ ref('dim_program') }}", ] ) }}