From 80ffd47778de33818351378c1e460ec09474caa3 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Sat, 14 Mar 2026 12:57:39 -0700 Subject: [PATCH 01/40] updating row number to rank --- .../acute_inpatient/acute_inpatient__generate_encounter_id.sql | 2 +- .../emergency_department__generate_encounter_id_pre_sort.sql | 2 +- .../inpatient_hospice__generate_encounter_id.sql | 2 +- .../inpatient_long_term__generate_encounter_id.sql | 2 +- .../inpatient_psych/inpatient_psych__generate_encounter_id.sql | 2 +- .../inpatient_rehab/inpatient_rehab__generate_encounter_id.sql | 2 +- .../inpatient_snf/inpatient_snf__generate_encounter_id.sql | 2 +- .../inpatient_substance_use__generate_encounter_id.sql | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql index 1954bd53f..a750076ff 100644 --- a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql @@ -38,7 +38,7 @@ select claim_id , end_date , discharge_disposition_code , facility_id - , row_number() over (partition by patient_data_source_id + , rank() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql index 537e4e8d0..ce70c003f 100644 --- a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql +++ b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql @@ -39,7 +39,7 @@ with claim_start_end as ( , facility_id , claim_type , case when claim_type = 'professional' then 1 else 0 end as is_professional - , row_number() over ( + , rank() over ( partition by patient_data_source_id order by end_date, start_date, claim_id ) as row_num diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql index cf4ee243d..5d159d995 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_id - , row_number() over (partition by patient_data_source_id + , rank() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql index 37bf3b0b2..6edd75c61 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_id - , row_number() over (partition by patient_data_source_id + , rank() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql index 8e5ee8399..8718cb5c7 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_id - , row_number() over (partition by patient_data_source_id + , rank() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql index 4f2ef38cb..4a332e0a3 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_id - , row_number() over (partition by patient_data_source_id + , rank() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql index a4c3a1fdd..219e6bf9e 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_id - , row_number() over (partition by patient_data_source_id + , rank() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql index df911c059..e2b7d392f 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_id - , row_number() over (partition by patient_data_source_id + , rank() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) From 0f513334dae617c6cf87454979cb7dcf4ba1b7f7 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 24 Mar 2026 11:36:00 -0700 Subject: [PATCH 02/40] updating end date --- .../emergency_department__generate_encounter_id_pre_sort.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql index 097e6b507..b27c5c033 100644 --- a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql +++ b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , c.end_date + , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined , enc.facility_npi , enc.discharge_disposition_code , enc.claim_type -- 'institutional' | 'professional' From c2e5ec412efcd4c35d71bcfbbc9a79fea6605076 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 24 Mar 2026 14:13:43 -0700 Subject: [PATCH 03/40] updating coalesce for end dates --- .../encounters/encounter_models.yml | 11 ++++++++++- .../acute_inpatient__generate_encounter_id.sql | 2 +- .../inpatient_hospice__generate_encounter_id.sql | 2 +- .../inpatient_long_term__generate_encounter_id.sql | 2 +- .../inpatient_psych__generate_encounter_id.sql | 2 +- .../inpatient_rehab__generate_encounter_id.sql | 2 +- .../inpatient_snf__generate_encounter_id.sql | 2 +- ...inpatient_substance_use__generate_encounter_id.sql | 2 +- 8 files changed, 17 insertions(+), 8 deletions(-) diff --git a/models/claims_preprocessing/encounters/encounter_models.yml b/models/claims_preprocessing/encounters/encounter_models.yml index 6b7f3a01b..80c3e0753 100644 --- a/models/claims_preprocessing/encounters/encounter_models.yml +++ b/models/claims_preprocessing/encounters/encounter_models.yml @@ -4477,7 +4477,16 @@ models: tags: - claims_preprocessing - encounters - + columns: + - name: end_date + description: The discharge date or end date of the claim if discharge date is null. + tests: + - not_null: + description: > + If this is null, the end date will default to the start date for encounters to ensure the encounter is not open-ended and + prevents grouping encounters far into the future. + config: + severity: warn - name: encounters__prof_and_lower_priority description: adding in lower priority institutional claims in one location with professional for grouping to higher level encounters config: diff --git a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql index d32fef871..89320a954 100644 --- a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql @@ -18,7 +18,7 @@ select claim_id enc.claim_id , enc.patient_data_source_id , c.start_date - , c.end_date + , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql index f877f593c..0eb7fc825 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , c.end_date + , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql index c822dd476..dbb5a57e4 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , c.end_date + , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql index 6728fa935..ef6bbd1e8 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , c.end_date + , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql index 4ed6f2b1f..839ac8a7e 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , c.end_date + , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql index 43cda99bc..f591033bd 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , c.end_date + , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql index 47d1ad5f9..2915a1692 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , c.end_date + , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc From 45fe4b81840f157b8399c13363a8a4612bfba314 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Fri, 10 Apr 2026 16:56:21 -0700 Subject: [PATCH 04/40] split end date update into separate PR --- .../acute_inpatient/acute_inpatient__generate_encounter_id.sql | 2 +- .../emergency_department__generate_encounter_id_pre_sort.sql | 2 +- .../inpatient_hospice__generate_encounter_id.sql | 2 +- .../inpatient_long_term__generate_encounter_id.sql | 2 +- .../inpatient_psych/inpatient_psych__generate_encounter_id.sql | 2 +- .../inpatient_rehab/inpatient_rehab__generate_encounter_id.sql | 2 +- .../inpatient_snf/inpatient_snf__generate_encounter_id.sql | 2 +- .../inpatient_substance_use__generate_encounter_id.sql | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql index 89320a954..151d2d907 100644 --- a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql @@ -18,7 +18,7 @@ select claim_id enc.claim_id , enc.patient_data_source_id , c.start_date - , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined + c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql index b27c5c033..31a3ea3c4 100644 --- a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql +++ b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined + c.end_date , enc.facility_npi , enc.discharge_disposition_code , enc.claim_type -- 'institutional' | 'professional' diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql index 0eb7fc825..1ba3f8250 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined + c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql index dbb5a57e4..8e4116391 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined + c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql index ef6bbd1e8..58392fb48 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined + c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql index 839ac8a7e..93969aef2 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined + c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql index f591033bd..c7819d440 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined + c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql index 2915a1692..4bc0bc718 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - , coalesce(c.end_date, c.start_date) as end_date -- Avoids large length of stay gaps being combined + c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc From 273fc6a1293f506aabfb2c28afe23ee1e906db00 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Fri, 10 Apr 2026 16:56:51 -0700 Subject: [PATCH 05/40] fix missing comma --- .../acute_inpatient/acute_inpatient__generate_encounter_id.sql | 2 +- .../emergency_department__generate_encounter_id_pre_sort.sql | 2 +- .../inpatient_hospice__generate_encounter_id.sql | 2 +- .../inpatient_long_term__generate_encounter_id.sql | 2 +- .../inpatient_psych/inpatient_psych__generate_encounter_id.sql | 2 +- .../inpatient_rehab/inpatient_rehab__generate_encounter_id.sql | 2 +- .../inpatient_snf/inpatient_snf__generate_encounter_id.sql | 2 +- .../inpatient_substance_use__generate_encounter_id.sql | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql index 151d2d907..d32fef871 100644 --- a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql @@ -18,7 +18,7 @@ select claim_id enc.claim_id , enc.patient_data_source_id , c.start_date - c.end_date + , c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql index 31a3ea3c4..097e6b507 100644 --- a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql +++ b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - c.end_date + , c.end_date , enc.facility_npi , enc.discharge_disposition_code , enc.claim_type -- 'institutional' | 'professional' diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql index 1ba3f8250..f877f593c 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - c.end_date + , c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql index 8e4116391..c822dd476 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - c.end_date + , c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql index 58392fb48..6728fa935 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - c.end_date + , c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql index 93969aef2..4ed6f2b1f 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - c.end_date + , c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql index c7819d440..43cda99bc 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - c.end_date + , c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql index 4bc0bc718..47d1ad5f9 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql @@ -18,7 +18,7 @@ with claim_start_end as ( enc.claim_id , enc.patient_data_source_id , c.start_date - c.end_date + , c.end_date , enc.facility_npi , enc.discharge_disposition_code from {{ ref('encounters__stg_medical_claim') }} as enc From 91612c19b9b12cfbb66334edafee94261d272d5a Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Fri, 10 Apr 2026 16:57:35 -0700 Subject: [PATCH 06/40] Updating yml config --- .../encounters/encounter_models.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/models/claims_preprocessing/encounters/encounter_models.yml b/models/claims_preprocessing/encounters/encounter_models.yml index 80c3e0753..c2d38c235 100644 --- a/models/claims_preprocessing/encounters/encounter_models.yml +++ b/models/claims_preprocessing/encounters/encounter_models.yml @@ -4477,16 +4477,6 @@ models: tags: - claims_preprocessing - encounters - columns: - - name: end_date - description: The discharge date or end date of the claim if discharge date is null. - tests: - - not_null: - description: > - If this is null, the end date will default to the start date for encounters to ensure the encounter is not open-ended and - prevents grouping encounters far into the future. - config: - severity: warn - name: encounters__prof_and_lower_priority description: adding in lower priority institutional claims in one location with professional for grouping to higher level encounters config: From 85e0eb7fe8e0d57226e165319f99d4e7ac01c79c Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Fri, 10 Apr 2026 16:58:42 -0700 Subject: [PATCH 07/40] formatting --- models/claims_preprocessing/encounters/encounter_models.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/claims_preprocessing/encounters/encounter_models.yml b/models/claims_preprocessing/encounters/encounter_models.yml index c2d38c235..a2ec9b3f7 100644 --- a/models/claims_preprocessing/encounters/encounter_models.yml +++ b/models/claims_preprocessing/encounters/encounter_models.yml @@ -4477,6 +4477,8 @@ models: tags: - claims_preprocessing - encounters + + - name: encounters__prof_and_lower_priority description: adding in lower priority institutional claims in one location with professional for grouping to higher level encounters config: From 65e8e5c61544bca65ca9d77b6a2ca7aac5e13fd1 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Fri, 10 Apr 2026 16:58:59 -0700 Subject: [PATCH 08/40] formatting --- models/claims_preprocessing/encounters/encounter_models.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/models/claims_preprocessing/encounters/encounter_models.yml b/models/claims_preprocessing/encounters/encounter_models.yml index a2ec9b3f7..6b7f3a01b 100644 --- a/models/claims_preprocessing/encounters/encounter_models.yml +++ b/models/claims_preprocessing/encounters/encounter_models.yml @@ -4478,7 +4478,6 @@ models: - claims_preprocessing - encounters - - name: encounters__prof_and_lower_priority description: adding in lower priority institutional claims in one location with professional for grouping to higher level encounters config: From 2f8390ed04219aad09cabb926f73629ba5cc1764 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 15 Apr 2026 09:05:24 -0700 Subject: [PATCH 09/40] Normalizing the facility NPI field --- ...lized_input__int_medical_npi_normalize.sql | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql index 4115646d2..a5d7576d7 100644 --- a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql +++ b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql @@ -4,6 +4,7 @@ ) }} +with base as ( select distinct med.claim_id , med.claim_line_number @@ -40,3 +41,32 @@ left outer join {{ ref('terminology__provider') }} as fac_prov on med.facility_npi = fac_prov.npi and fac_prov.entity_type_description = 'Organization' and med.claim_type = 'institutional' +) + +, facility_npi_ranked as ( +select + claim_id + , normalized_facility_npi + , normalized_facility_name + , row_number() over (partition by claim_id order by claim_line_number) as facility_npi_rank +from base +where normalized_facility_npi is not null +) + +select + base.claim_id + , base.claim_line_number + , base.claim_start_date + , base.claim_type + , base.data_source + , base.normalized_rendering_npi + , base.normalized_rendering_name + , base.normalized_billing_npi + , base.normalized_billing_name + , fac.normalized_facility_npi + , fac.normalized_facility_name + , base.tuva_last_run +from base +left join facility_npi_ranked as fac + on base.claim_id = fac.claim_id + and facility_npi_rank = 1 \ No newline at end of file From 558c7a7a2929dff49d1c30d596fb530aecdc0f1a Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 15 Apr 2026 09:07:04 -0700 Subject: [PATCH 10/40] removing claim start date --- .../intermediate/normalized_input__int_medical_npi_normalize.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql index a5d7576d7..e4759e587 100644 --- a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql +++ b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql @@ -56,7 +56,6 @@ where normalized_facility_npi is not null select base.claim_id , base.claim_line_number - , base.claim_start_date , base.claim_type , base.data_source , base.normalized_rendering_npi From d3af5695ea3db8ebb7e6540f87917690018997ce Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 15 Apr 2026 09:26:20 -0700 Subject: [PATCH 11/40] Updating tests to use test prefix + normalizing facility NPI --- .../claims_preprocessing_models.yml | 2 +- ...l => test_claim_type_not_only_undetermined.sql} | 0 tests/test_facility_npi_claim_uniqueness.sql | 14 ++++++++++++++ ...st_institutional_diagnosis_code_uniqueness.sql} | 0 ..._amount.sql => test_medical_allowed_amount.sql} | 0 ... => test_medical_claim_eligibility_overlap.sql} | 0 ...amount.sql => test_pharmacy_allowed_amount.sql} | 0 ...=> test_pharmacy_claim_eligibility_overlap.sql} | 0 ...heck_plan_overlap.sql => test_plan_overlap.sql} | 0 ...eck.sql => test_pmpm_paid_amount_threshold.sql} | 0 10 files changed, 15 insertions(+), 1 deletion(-) rename tests/{claim_type_not_only_undetermined_check.sql => test_claim_type_not_only_undetermined.sql} (100%) create mode 100644 tests/test_facility_npi_claim_uniqueness.sql rename tests/{check_institutional_diagnosis_code_uniqueness.sql => test_institutional_diagnosis_code_uniqueness.sql} (100%) rename tests/{check_medical_allowed_amount.sql => test_medical_allowed_amount.sql} (100%) rename tests/{medical_claim_eligibility_overlap_check.sql => test_medical_claim_eligibility_overlap.sql} (100%) rename tests/{check_pharmacy_allowed_amount.sql => test_pharmacy_allowed_amount.sql} (100%) rename tests/{pharmacy_claim_eligibility_overlap_check.sql => test_pharmacy_claim_eligibility_overlap.sql} (100%) rename tests/{check_plan_overlap.sql => test_plan_overlap.sql} (100%) rename tests/{pmpm_paid_amount_threshold_check.sql => test_pmpm_paid_amount_threshold.sql} (100%) diff --git a/models/claims_preprocessing/claims_preprocessing_models.yml b/models/claims_preprocessing/claims_preprocessing_models.yml index 66daedec8..f073a6f56 100644 --- a/models/claims_preprocessing/claims_preprocessing_models.yml +++ b/models/claims_preprocessing/claims_preprocessing_models.yml @@ -277,7 +277,7 @@ models: combination_of_columns: - claim_id - claim_line_number - - data_source + - data_source columns: - name: claim_id description: Unique identifier for each claim. diff --git a/tests/claim_type_not_only_undetermined_check.sql b/tests/test_claim_type_not_only_undetermined.sql similarity index 100% rename from tests/claim_type_not_only_undetermined_check.sql rename to tests/test_claim_type_not_only_undetermined.sql diff --git a/tests/test_facility_npi_claim_uniqueness.sql b/tests/test_facility_npi_claim_uniqueness.sql new file mode 100644 index 000000000..3eebe11d8 --- /dev/null +++ b/tests/test_facility_npi_claim_uniqueness.sql @@ -0,0 +1,14 @@ +with base as ( +select distinct + claim_id + , facility_npi +from {{ ref('normalized_input__medical_claim') }} +) + +select + claim_id + , count(*) +from base +group by + claim_id +having count(*) > 1 diff --git a/tests/check_institutional_diagnosis_code_uniqueness.sql b/tests/test_institutional_diagnosis_code_uniqueness.sql similarity index 100% rename from tests/check_institutional_diagnosis_code_uniqueness.sql rename to tests/test_institutional_diagnosis_code_uniqueness.sql diff --git a/tests/check_medical_allowed_amount.sql b/tests/test_medical_allowed_amount.sql similarity index 100% rename from tests/check_medical_allowed_amount.sql rename to tests/test_medical_allowed_amount.sql diff --git a/tests/medical_claim_eligibility_overlap_check.sql b/tests/test_medical_claim_eligibility_overlap.sql similarity index 100% rename from tests/medical_claim_eligibility_overlap_check.sql rename to tests/test_medical_claim_eligibility_overlap.sql diff --git a/tests/check_pharmacy_allowed_amount.sql b/tests/test_pharmacy_allowed_amount.sql similarity index 100% rename from tests/check_pharmacy_allowed_amount.sql rename to tests/test_pharmacy_allowed_amount.sql diff --git a/tests/pharmacy_claim_eligibility_overlap_check.sql b/tests/test_pharmacy_claim_eligibility_overlap.sql similarity index 100% rename from tests/pharmacy_claim_eligibility_overlap_check.sql rename to tests/test_pharmacy_claim_eligibility_overlap.sql diff --git a/tests/check_plan_overlap.sql b/tests/test_plan_overlap.sql similarity index 100% rename from tests/check_plan_overlap.sql rename to tests/test_plan_overlap.sql diff --git a/tests/pmpm_paid_amount_threshold_check.sql b/tests/test_pmpm_paid_amount_threshold.sql similarity index 100% rename from tests/pmpm_paid_amount_threshold_check.sql rename to tests/test_pmpm_paid_amount_threshold.sql From 3966b16169ddf27ef86e22d6a6563e3501bdd0fe Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Mon, 20 Apr 2026 09:42:02 -0700 Subject: [PATCH 12/40] Adding data source constraint --- .../normalized_input__int_medical_npi_normalize.sql | 6 ++++-- tests/test_facility_npi_claim_uniqueness.sql | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql index e4759e587..ad378b006 100644 --- a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql +++ b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql @@ -45,10 +45,11 @@ left outer join {{ ref('terminology__provider') }} as fac_prov , facility_npi_ranked as ( select - claim_id + data_source + , claim_id , normalized_facility_npi , normalized_facility_name - , row_number() over (partition by claim_id order by claim_line_number) as facility_npi_rank + , row_number() over (partition by data_source, claim_id order by claim_line_number) as facility_npi_rank from base where normalized_facility_npi is not null ) @@ -68,4 +69,5 @@ select from base left join facility_npi_ranked as fac on base.claim_id = fac.claim_id + and base.data_source = fac.data_source and facility_npi_rank = 1 \ No newline at end of file diff --git a/tests/test_facility_npi_claim_uniqueness.sql b/tests/test_facility_npi_claim_uniqueness.sql index 3eebe11d8..7ee740c24 100644 --- a/tests/test_facility_npi_claim_uniqueness.sql +++ b/tests/test_facility_npi_claim_uniqueness.sql @@ -1,14 +1,17 @@ with base as ( select distinct claim_id + , data_source , facility_npi from {{ ref('normalized_input__medical_claim') }} ) select claim_id + , data_source , count(*) from base group by claim_id + , data_source having count(*) > 1 From dd7bd208a5bc3f70897e2543ebebdac0ff1e92d0 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 11:40:12 -0700 Subject: [PATCH 13/40] update hcc recapture to include suspect HCCs --- .../final/hcc_recapture__gap_status.sql | 8 +- .../final/hcc_recapture__hcc_status.sql | 84 ++++-- ...hcc_recapture__recapture_rates_monthly.sql | 21 +- .../hcc_recapture__int_all_hccs.sql | 166 ----------- ...cc_recapture__int_determine_gap_status.sql | 161 ++++++++++ .../hcc_recapture__int_eligible_benes.sql | 9 + .../hcc_recapture__int_gap_status.sql | 281 +++++------------- .../hccs/hcc_recapture__int_all_hccs.sql | 105 +++++++ .../hccs/hcc_recapture__int_coded_hccs.sql | 34 +++ .../hcc_recapture__int_recapturable_hccs.sql} | 27 +- .../hccs/hcc_recapture__int_suspect_hccs.sql | 28 ++ .../hcc_recapture__stg_eligible_benes.sql | 3 +- .../hcc_recapture__stg_suspect_hccs.sql | 19 ++ .../hcc_recapture/staging_models.yml | 11 + 14 files changed, 521 insertions(+), 436 deletions(-) delete mode 100644 models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_all_hccs.sql create mode 100644 models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql create mode 100644 models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_eligible_benes.sql create mode 100644 models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql create mode 100644 models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql rename models/data_marts/hcc_recapture/intermediate/{hcc_recapture__int_hccs.sql => hccs/hcc_recapture__int_recapturable_hccs.sql} (73%) create mode 100644 models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_suspect_hccs.sql create mode 100644 models/data_marts/hcc_recapture/staging/hcc_recapture__stg_suspect_hccs.sql diff --git a/models/data_marts/hcc_recapture/final/hcc_recapture__gap_status.sql b/models/data_marts/hcc_recapture/final/hcc_recapture__gap_status.sql index ea0b4a2a2..3bb23703d 100644 --- a/models/data_marts/hcc_recapture/final/hcc_recapture__gap_status.sql +++ b/models/data_marts/hcc_recapture/final/hcc_recapture__gap_status.sql @@ -11,9 +11,11 @@ select distinct , risk_model_code , model_version , payment_year - , recapture_flag + , recapturable_flag + , hcc_type + , hcc_source , gap_status , suspect_hcc_flag -from {{ ref('hcc_recapture__int_gap_status') }} +from {{ ref('hcc_recapture__int_gap_status')}} -- Apply hierarchies -where filtered_out_by_hierarchy = 0 +where filtered_by_hierarchy_flag = 0 diff --git a/models/data_marts/hcc_recapture/final/hcc_recapture__hcc_status.sql b/models/data_marts/hcc_recapture/final/hcc_recapture__hcc_status.sql index 2ec28a67b..8ce06dac4 100644 --- a/models/data_marts/hcc_recapture/final/hcc_recapture__hcc_status.sql +++ b/models/data_marts/hcc_recapture/final/hcc_recapture__hcc_status.sql @@ -4,34 +4,56 @@ }} select distinct - stg.person_id - , stg.payer - , stg.data_source - , coalesce(gap.payment_year, {{ date_part('year', 'recorded_date') }} + 1) as payment_year - , stg.recorded_date - , stg.claim_id - , stg.rendering_npi - , stg.model_version - , stg.hcc_code - , stg.hcc_description - , stg.hcc_hierarchy_group - , stg.hcc_hierarchy_group_rank - , stg.suspect_hcc_flag - , stg.risk_model_code - , stg.eligible_claim_indicator - , stg.eligible_bene - , stg.reason - , gap.gap_status - , gap.recapture_flag -from {{ ref('hcc_recapture__int_hccs') }} as stg -left outer join {{ ref('hcc_recapture__gap_status') }} as gap - on stg.person_id = gap.person_id - and stg.payer = gap.payer - and stg.model_version = gap.model_version - and stg.hcc_code = gap.hcc_code - and stg.suspect_hcc_flag = gap.suspect_hcc_flag - and (case - when gap.gap_status = 'open' then stg.collection_year + 2 - else stg.collection_year + 1 - end) = gap.payment_year -where eligible_bene = 1 + hccs.person_id + , hccs.payer + , hccs.data_source + , coalesce(gap.payment_year, {{ date_part('year', 'hccs.recorded_date') }} + 1) as payment_year + , hccs.recorded_date + , hccs.claim_id + , hccs.rendering_npi + , hccs.model_version + , hccs.hcc_code + , hccs.hcc_description + , hccs.hcc_hierarchy_group + , hccs.hcc_hierarchy_group_rank + , hccs.suspect_hcc_flag + -- Latest risk_model_code per person/year/model_version based on recorded_date + , first_value(hccs.risk_model_code) over ( + partition by + hccs.person_id, + coalesce(gap.payment_year, {{ date_part('year', 'hccs.recorded_date') }} + 1), + hccs.model_version + order by hccs.recorded_date desc + ) as risk_model_code + , hccs.hcc_type + , hccs.hcc_source + , coalesce(gap.gap_status,'ineligible for recapture') as gap_status + -- Filters that may lead to an 'ineligible for recapture' gap status + , hccs.hcc_chronic_flag + , hccs.recapturable_flag + , hccs.eligible_claim_flag + , hccs.eligible_bene_flag + , coalesce(gap.filtered_by_hierarchy_flag, recap.filtered_by_hierarchy_flag,0) as filtered_by_hierarchy_flag +from {{ ref('hcc_recapture__int_all_hccs') }} as hccs +left join {{ ref('hcc_recapture__int_recapturable_hccs') }} as recap + on hccs.person_id = recap.person_id + and hccs.hcc_code = recap.hcc_code + and hccs.data_source = recap.data_source + and hccs.payer = recap.payer + and hccs.model_version = recap.model_version + and hccs.collection_year = recap.collection_year + and hccs.hcc_hierarchy_group = recap.hcc_hierarchy_group + and coalesce(hccs.claim_id, '') = coalesce(recap.claim_id, '') +left join {{ ref('hcc_recapture__int_gap_status') }} as gap + on + hccs.person_id = gap.person_id + and hccs.payer = gap.payer + and hccs.model_version = gap.model_version + and hccs.hcc_code = gap.hcc_code + -- For TUVA gaps, +2 is needed because we’re comparing collection year to payment year - we already need a +1 for that comparison, and an additional +1 to account for closure in the following year. + -- For suspect HCCs, we only apply +1, since it’s ok for those HCCs to close themselves within the same year (e.g., CY 2025 suspect list HCCs can be closed in CY 2025 based on claims rather than CY 2026). + and (case + when gap.gap_status = 'open' and hccs.hcc_type = 'coded' then hccs.collection_year + 2 + else hccs.collection_year + 1 + end) = gap.payment_year +where hccs.eligible_bene_flag = 1 \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql b/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql index 90a3e2e94..e29af7598 100644 --- a/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql +++ b/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql @@ -9,27 +9,22 @@ select distinct person_id , payer , payment_year - , cast({{ concat_custom([ - "payment_year" - , "'-'" - , date_part('month', 'recorded_date') - , "'-'" - , "'1'" - ]) }} as date) as payment_year_month + , date_from_parts(payment_year, month(recorded_date),1) as payment_year_month , recorded_date , model_version , hcc_code , gap_status - , recapture_flag + , recapturable_flag , row_number() over (partition by person_id, payer, payment_year, model_version, hcc_code order by recorded_date asc) as earliest_hcc_code -from {{ ref('hcc_recapture__hcc_status') }} -where gap_status not in ('inappropriate for recapture', 'new') - and gap_status is not null - and suspect_hcc_flag = 0 +from {{ ref('hcc_recapture__hcc_status')}} +where 1=1 + and gap_status not in ('ineligible for recapture', 'new') + and hcc_type in ('captured', 'coded') + and recapturable_flag = 1 ) , monthly_hcc_counts as ( -select +select payer , payment_year , payment_year_month diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_all_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_all_hccs.sql deleted file mode 100644 index 5ad7970ff..000000000 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_all_hccs.sql +++ /dev/null @@ -1,166 +0,0 @@ -{{ config( - enabled = var('claims_enabled', False) | as_bool - ) -}} - -with seed_hcc_hierarchy as ( - select - model_version - , hcc_code - , hcc_hierarchy_group - , hcc_hierarchy_group_rank - from {{ ref('cms_hcc__disease_hierarchy_flat') }} -) - -, hcc_diagnosis as ( - select - payment_year - , diagnosis_code - , cms_hcc_v28 as hcc_code - , 'CMS-HCC-V28' as model_version - from {{ ref('cms_hcc__icd_10_cm_mappings') }} - where cms_hcc_v28_flag = 'Yes' - - union all - - select - payment_year - , diagnosis_code - , cms_hcc_v24 as hcc_code - , 'CMS-HCC-V24' as model_version - from {{ ref('cms_hcc__icd_10_cm_mappings') }} - where cms_hcc_v24_flag = 'Yes' -) - -, chronic_hccs as ( -select distinct - diag.hcc_code - , diag.payment_year - , diag.model_version - , 1 as chronic_flag -from {{ ref('chronic_conditions__cms_chronic_conditions_hierarchy') }} as hier -inner join hcc_diagnosis as diag - on hier.code = diag.diagnosis_code -) - -, get_risk_code as ( -select distinct - person_id - , payer - , payment_year - , model_version - , risk_model_code - , row_number() over (partition by person_id, payment_year, model_version order by collection_end_date desc) as month_order -from {{ ref('cms_hcc__int_demographic_factors') }} -where lower(factor_type) = 'demographic' -) - -, eligible_claims as ( --- Use distinct to remove claim line -select distinct - person_id - , claim_id - , payer -from {{ ref('cms_hcc__int_eligible_conditions') }} -) - -, medical_claims as ( --- Use distinct to remove claim line -select distinct - person_id - , payer - , claim_id - , rendering_npi -from {{ ref('core__medical_claim') }} -) - -, include_suspect_hccs as ( -select - person_id - , payer - , data_source - , recorded_date - , model_version - , claim_id - , hcc_code - , hcc_description - , condition_type - -- Listed as prior coding history since it comes from int_all_conditions, see hcc_suspecting__int_patient_hcc_history for reference - , 'Prior coding history' as reason - , 0 as suspect_hcc_flag --- Not using list_all since it doesn't have claim_id pulled through --- TODO: Update hcc_suspecting__list_all to have claim ID as well -from {{ ref('hcc_suspecting__int_all_conditions') }} -union all -select - person_id - , payer - , data_source - , suspect_date as recorded_date - , model_version - , null as claim_id - , hcc_code - , hcc_description - , 'suspect' as condition_type - , reason - , 1 as suspect_hcc_flag -from {{ ref('hcc_suspecting__list_all') }} --- Exclude since already included in int_all_conditions -where lower(reason) != 'prior coding history' -) - --- NOTE: Distinct is to remove different recording dates + ICD 10 codes for the same HCC code -select distinct - sus.person_id - , sus.payer - , sus.data_source - , {{ date_part('year', 'sus.recorded_date') }} as collection_year - , sus.recorded_date - , sus.model_version - , sus.claim_id - , sus.hcc_code - , sus.hcc_description - , chronic.chronic_flag as hcc_chronic_flag - , coalesce(hier.hcc_hierarchy_group, 'no hierarchy') as hcc_hierarchy_group - , coalesce(hier.hcc_hierarchy_group_rank, 1) as hcc_hierarchy_group_rank - , rcode.risk_model_code - , sus.condition_type - , case - when elig.claim_id is not null then 1 - when sus.suspect_hcc_flag = 1 then 1 - else 0 - end as eligible_claim_indicator - , case when elig_bene.person_id is not null then 1 else 0 end as eligible_bene - , med.rendering_npi - , reason - , suspect_hcc_flag -from include_suspect_hccs as sus -left outer join seed_hcc_hierarchy as hier - on sus.hcc_code = hier.hcc_code - and sus.model_version = hier.model_version -left outer join chronic_hccs as chronic - on sus.model_version = chronic.model_version - and sus.hcc_code = chronic.hcc_code - and {{ date_part('year', 'sus.recorded_date') }} = chronic.payment_year - 1 -left outer join get_risk_code as rcode - on sus.person_id = rcode.person_id - and sus.payer = rcode.payer - and {{ date_part('year', 'sus.recorded_date') }} = rcode.payment_year - 1 - and sus.model_version = rcode.model_version - and rcode.month_order = 1 -left outer join eligible_claims as elig - on sus.person_id = elig.person_id - and sus.payer = elig.payer - and sus.claim_id = elig.claim_id -left outer join medical_claims as med - on sus.person_id = med.person_id - and sus.payer = med.payer - and sus.claim_id = med.claim_id --- Only include benes eligible for gap closure -left outer join {{ ref('hcc_recapture__stg_eligible_benes') }} as elig_bene - on sus.person_id = elig_bene.person_id - and {{ date_part('year', 'sus.recorded_date') }} = elig_bene.collection_year - and sus.payer = elig_bene.payer -where sus.hcc_code is not null - -- Replace with cms_hcc__adjustment_rates once that table includes PY 2026 - and 1 = (case when {{ date_part('year', 'sus.recorded_date') }} >= 2025 and sus.model_version = 'CMS-HCC-V24' then 0 else 1 end) diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql new file mode 100644 index 000000000..10ad013bd --- /dev/null +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql @@ -0,0 +1,161 @@ +-- Get recapturable HCCs within the past 1 year +with filtered_hccs as ( + select * + from {{ ref('hcc_recapture__int_recapturable_hccs') }} + where filtered_by_hierarchy_flag = 0 +), + +risk_gaps as ( + select distinct + person_id + , payer + , collection_year + 1 as collection_year + , model_version + , hcc_code + , hcc_hierarchy_group + , hcc_hierarchy_group_rank + , suspect_hcc_flag + , recapturable_flag + , hcc_type + , hcc_source + , eligible_bene_flag + , risk_model_code + from filtered_hccs + where hcc_type = 'coded' + + union all + + -- No need to add +1 to collection year since these are already identified as captured/suspect in the same year identified + -- These come from other sources besides claims, such as payers + clinical data + select distinct + person_id + , payer + , collection_year + , model_version + , hcc_code + , hcc_hierarchy_group + , hcc_hierarchy_group_rank + , suspect_hcc_flag + , recapturable_flag + , hcc_type + , hcc_source + , eligible_bene_flag + , risk_model_code + from filtered_hccs + where hcc_type = 'suspect' +), + +best_past_rank as ( + select distinct + person_id + , payer + , collection_year + , model_version + , hcc_code + , hcc_hierarchy_group + , hcc_hierarchy_group_rank + , min(hcc_hierarchy_group_rank) over (partition by person_id, payer, collection_year, model_version, hcc_hierarchy_group) as best_past_rank + from filtered_hccs + where hcc_type in ('coded', 'captured') +), + + +best_current_rank as ( + select distinct + person_id + , payer + , collection_year + , model_version + , hcc_code + , hcc_hierarchy_group + , hcc_hierarchy_group_rank + , risk_model_code + , eligible_bene_flag + , min(hcc_hierarchy_group_rank) over (partition by person_id, payer, collection_year, model_version, hcc_hierarchy_group) as best_current_rank + from filtered_hccs +), + +equiv_coef as ( + select distinct + base.model_version + , base.hcc_hierarchy_group + , base.hcc_code + , base.risk_model_code + from {{ ref('hcc_recapture__stg_coef_hier') }} as base + inner join {{ ref('hcc_recapture__stg_coef_hier') }} as self + on + base.hcc_hierarchy_group = self.hcc_hierarchy_group + and base.risk_model_code = self.risk_model_code + and base.coefficient = self.coefficient + and base.model_version = self.model_version + and base.hcc_code != self.hcc_code +) + +-- Note: Gaps can only be closed using claims received from the payor or from Athena. +select + coalesce(base.payer, gap.payer) as payer + , coalesce(base.person_id, gap.person_id) as person_id + , coalesce(base.risk_model_code, gap.risk_model_code, current_year_hier.risk_model_code) as risk_model_code + , coalesce(base.eligible_bene_flag, gap.eligible_bene_flag, current_year_hier.eligible_bene_flag) as eligible_bene_flag + , coalesce(base.hcc_code, gap.hcc_code) as hcc_code + , gap.hcc_code as recaptured_hcc_code + , current_year_hier.hcc_code as current_year_hcc_code + , grp.hcc_code as past_year_hcc_code + , coalesce(base.suspect_hcc_flag, gap.suspect_hcc_flag, 0) as suspect_hcc_flag + , coalesce(base.model_version, gap.model_version) as model_version + , coalesce(base.collection_year, gap.collection_year) as collection_year + , coalesce(base.hcc_hierarchy_group, gap.hcc_hierarchy_group) as hcc_hierarchy_group + , coalesce(base.hcc_hierarchy_group_rank, gap.hcc_hierarchy_group_rank) as hcc_hierarchy_group_rank + , coalesce(base.recapturable_flag, gap.recapturable_flag) as recapturable_flag + , coalesce(base.hcc_type, gap.hcc_type) as hcc_type + , coalesce(base.hcc_source, gap.hcc_source) as hcc_source + , case + when + gap.hcc_code is not null and base.hcc_code is not null and gap.hcc_code != base.hcc_code and equiv.risk_model_code is not null + then 'closed - equivalent coefficient hcc in hierarchy group' + when + grp.hcc_hierarchy_group is not null and base.hcc_hierarchy_group_rank < grp.best_past_rank + then 'closed - higher coefficient hcc in hierarchy group' + when current_year_hier.best_current_rank < gap.hcc_hierarchy_group_rank then 'closed - higher coefficient hcc in hierarchy group' + when gap.hcc_code is not null and base.hcc_code is not null then 'closed' + when + grp.hcc_hierarchy_group is not null and base.hcc_hierarchy_group_rank > grp.best_past_rank + then 'closed - lower coefficient hcc in hierarchy group' + when gap.hcc_code is not null and base.hcc_code is null then 'open' + when gap.hcc_code is null and base.hcc_code is not null then 'new' + end as gap_status +from filtered_hccs as base +full outer join risk_gaps as gap + on + base.person_id = gap.person_id + and base.payer = gap.payer + and base.collection_year = gap.collection_year + and base.model_version = gap.model_version + and base.hcc_code = gap.hcc_code + -- Only coded or captured HCCs can close other HCCs + and base.hcc_type in ('coded', 'captured') +left join equiv_coef as equiv + on + base.model_version = equiv.model_version + and base.hcc_hierarchy_group = equiv.hcc_hierarchy_group + and base.hcc_code = equiv.hcc_code + and base.risk_model_code = equiv.risk_model_code +left join best_past_rank as grp + on + base.person_id = grp.person_id + and base.payer = grp.payer + and base.collection_year = grp.collection_year + and base.model_version = grp.model_version + and base.hcc_hierarchy_group = grp.hcc_hierarchy_group + and grp.best_past_rank = grp.hcc_hierarchy_group_rank +left join best_current_rank as current_year_hier + on + gap.person_id = current_year_hier.person_id + and gap.payer = current_year_hier.payer + and gap.collection_year = current_year_hier.collection_year + and gap.model_version = current_year_hier.model_version + and gap.hcc_hierarchy_group = current_year_hier.hcc_hierarchy_group + and current_year_hier.best_current_rank = current_year_hier.hcc_hierarchy_group_rank +-- Gaps are only eligible to be closed by claims data and the base table here is closing the gap aliased table +-- The or hcc_type is null allows open HCCs to flow through +where base.hcc_type in ('coded', 'captured') or base.hcc_type is null diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_eligible_benes.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_eligible_benes.sql new file mode 100644 index 000000000..6cd0504ae --- /dev/null +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_eligible_benes.sql @@ -0,0 +1,9 @@ +-- Flattening months to 1 person per year +select distinct + person_id + , collection_year + , payer +from {{ ref('hcc_recapture__stg_eligible_benes') }} +-- Null age groups leads to null risk model codes. +-- Risk model codes needs not to be null since it's used as a join argument to get equivalent coefficients. +where age_group is not null \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql index 14dc8ffc5..5728cac2d 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql @@ -1,233 +1,102 @@ -{{ config( - enabled = var('claims_enabled', False) | as_bool - ) -}} - -with eligible_hccs as ( - select - * - from {{ ref('hcc_recapture__int_hccs') }} -) - --- Get recapturable HCCs within the prior year -, recapturable_hccs as ( -select distinct - person_id - , payer - , collection_year + 1 as collection_year - , model_version - , hcc_code - , hcc_hierarchy_group - , hcc_hierarchy_group_rank - , suspect_hcc_flag -from eligible_hccs -) - -, best_past_rank as ( -select distinct - person_id - , payer - , collection_year - , model_version - , hcc_code - , hcc_hierarchy_group - , hcc_hierarchy_group_rank - , min(hcc_hierarchy_group_rank) over (partition by person_id, payer, collection_year, model_version, hcc_hierarchy_group) as best_past_rank -from recapturable_hccs --- This is only for what was actually coded in the past -where suspect_hcc_flag = 0 -) - - -, best_current_rank as ( -select distinct - person_id - , payer - , collection_year - , model_version - , hcc_code - , hcc_hierarchy_group - , hcc_hierarchy_group_rank - , min(hcc_hierarchy_group_rank) over (partition by person_id, payer, collection_year, model_version, hcc_hierarchy_group) as best_current_rank -from eligible_hccs -) - -, equiv_coef as ( -select distinct - base.model_version - , base.hcc_hierarchy_group - , base.hcc_code - , base.risk_model_code -from {{ ref('hcc_recapture__stg_coef_hier') }} as base -inner join {{ ref('hcc_recapture__stg_coef_hier') }} as self - on base.hcc_hierarchy_group = self.hcc_hierarchy_group - and base.risk_model_code = self.risk_model_code - and base.coefficient = self.coefficient - and base.model_version = self.model_version - and base.hcc_code != self.hcc_code -) - -, add_gap_status as ( -select - coalesce(recap.person_id, base.person_id) as person_id - , coalesce(recap.payer, base.payer) as payer - , coalesce(recap.hcc_code, base.hcc_code) as hcc_code - , coalesce(recap.suspect_hcc_flag, 0) as suspect_hcc_flag - , recap.hcc_code as recaptured_hcc_code - , current_year_hier.hcc_code as current_year_hcc_code - , grp.hcc_code as past_year_hcc_code - , coalesce(recap.model_version, base.model_version) as model_version - , coalesce(recap.collection_year, base.collection_year) as collection_year - , coalesce(recap.hcc_hierarchy_group, base.hcc_hierarchy_group) as hcc_hierarchy_group - , coalesce(recap.hcc_hierarchy_group_rank, base.hcc_hierarchy_group_rank) as hcc_hierarchy_group_rank - , base.risk_model_code - , case when recap.hcc_code is not null or grp.hcc_hierarchy_group is not null or base.hcc_chronic_flag = 1 then 1 else 0 end as recapture_flag - , eligible_bene - , case - when base.hcc_chronic_flag = 0 then 'inappropriate for recapture' - when recap.hcc_code is not null and base.hcc_code is not null and recap.hcc_code != base.hcc_code and equiv.risk_model_code is not null then 'closed - equivalent coefficient hcc in hierarchy group' - when grp.hcc_hierarchy_group is not null and base.hcc_hierarchy_group_rank < grp.best_past_rank then 'closed - higher coefficient hcc in hierarchy group' - when current_year_hier.best_current_rank < recap.hcc_hierarchy_group_rank then 'closed - higher coefficient hcc in hierarchy group' - when recap.hcc_code is not null and base.hcc_code is not null then 'closed' - when grp.hcc_hierarchy_group is not null and base.hcc_hierarchy_group_rank > grp.best_past_rank then 'closed - lower coefficient hcc in hierarchy group' - -- TODO: Should the current_year_hier be used to determine if something was closed by a lower coefficient hcc? - -- This was previously removed due to errors in primary key checks it was causing - when recap.hcc_code is not null and base.hcc_code is null then 'open' - when recap.hcc_code is null and base.hcc_code is not null and base.hcc_chronic_flag = 1 then 'new' - end as gap_status -from eligible_hccs as base -full outer join recapturable_hccs as recap - on base.person_id = recap.person_id - and base.payer = recap.payer - and base.collection_year = recap.collection_year - and base.model_version = recap.model_version - and base.hcc_code = recap.hcc_code -left outer join equiv_coef as equiv - on base.model_version = equiv.model_version - and base.hcc_hierarchy_group = equiv.hcc_hierarchy_group - and base.hcc_code = equiv.hcc_code - and base.risk_model_code = equiv.risk_model_code -left outer join best_past_rank as grp - on base.person_id = grp.person_id - and base.payer = grp.payer - and base.collection_year = grp.collection_year - and base.model_version = grp.model_version - and base.hcc_hierarchy_group = grp.hcc_hierarchy_group - and grp.best_past_rank = grp.hcc_hierarchy_group_rank -left outer join best_current_rank as current_year_hier - on recap.person_id = current_year_hier.person_id - and recap.payer = current_year_hier.payer - and recap.collection_year = current_year_hier.collection_year - and recap.model_version = current_year_hier.model_version - and recap.hcc_hierarchy_group = current_year_hier.hcc_hierarchy_group - and current_year_hier.best_current_rank = current_year_hier.hcc_hierarchy_group_rank --- Filtering to just discharge diagnosis since gaps are only eligible to be closed by claims data and the base table here is closing the recap aliased table --- The or condition_type is null allows open HCCs to flow through -where lower(base.condition_type) = 'discharge_diagnosis' or base.condition_type is null -) - -- Need to do this for HCCs in more than 1 group such as HCC 409 in v28 -, rank_gap_status as ( -select - person_id - , payer - , hcc_code - , suspect_hcc_flag - , model_version - , collection_year + 1 as payment_year - , recapture_flag - , eligible_bene - , gap_status - , risk_model_code - , hcc_hierarchy_group - , hcc_hierarchy_group_rank - -- just used for deduping - , case gap_status - when 'inappropriate for recapture' then 1 - when 'closed - equivalent coefficient hcc in hierarchy group' then 2 - when 'closed - higher coefficient hcc in hierarchy group' then 3 - when 'closed' then 4 - when 'closed - lower coefficient hcc in hierarchy group' then 5 - when 'open' then 6 - when 'new' then 7 - end as gap_status_rank -from add_gap_status +with add_rankings as ( + select + person_id + , payer + , hcc_code + , suspect_hcc_flag + , model_version + , collection_year + 1 as payment_year + , recapturable_flag + , hcc_type + , hcc_source + , eligible_bene_flag + , gap_status + , risk_model_code + , hcc_hierarchy_group + , hcc_hierarchy_group_rank + , case when hcc_type = 'coded' then 1 when hcc_type = 'captured' then 2 else 3 end as hcc_type_rank + -- just used for deduping + , case gap_status + when 'closed - equivalent coefficient hcc in hierarchy group' then 1 + when 'closed - higher coefficient hcc in hierarchy group' then 2 + when 'closed' then 3 + when 'closed - lower coefficient hcc in hierarchy group' then 4 + when 'new' then 5 -- New comes before open since suspects will always be open, but coded will sometimes say it is new + when 'open' then 6 + end as gap_status_rank + from {{ ref('hcc_recapture__int_determine_gap_status') }} ) -, min_gap_status as ( -select - person_id - , payer - , hcc_code - , suspect_hcc_flag - , model_version - , payment_year - , risk_model_code - , recapture_flag - , eligible_bene - , gap_status - , gap_status_rank - , hcc_hierarchy_group - , hcc_hierarchy_group_rank - , min(gap_status_rank) over (partition by person_id, payer, hcc_code, model_version, payment_year) as min_gap_status_rank -from rank_gap_status +-- Pick the best hcc type + -- 1. Best rank + -- 2. If tie → prefer recapture (suspect_hcc_flag = 0) +, best_hcc_type as ( + select * + from add_rankings + qualify row_number() over ( + partition by person_id, payer, hcc_code, model_version, payment_year + order by + hcc_type_rank asc, + suspect_hcc_flag asc -- 0 preferred over 1 + ) = 1 ) -- Pick the best gap status , best_gap_status as ( -select - * -from min_gap_status -where min_gap_status_rank = gap_status_rank + select * + from best_hcc_type + qualify min(gap_status_rank) over (partition by person_id, payer, hcc_code, model_version, payment_year) = gap_status_rank ) -- Find the minimum hierarchy for open hccs , min_open_hierarchy as ( -select - person_id - , payer - , payment_year - , model_version - , hcc_hierarchy_group - , suspect_hcc_flag - , min(hcc_hierarchy_group_rank) as min_hcc_hier_group_rank -from best_gap_status -group by - person_id - , payer - , payment_year - , model_version - , hcc_hierarchy_group - , suspect_hcc_flag + select + person_id + , payer + , payment_year + , model_version + , hcc_hierarchy_group + , min(hcc_hierarchy_group_rank) as min_hcc_hier_group_rank + from best_gap_status + group by + person_id + , payer + , payment_year + , model_version + , hcc_hierarchy_group ) -- Using distinct to deduplicate select distinct - bgap.person_id + bgap.person_id , bgap.payer , bgap.hcc_code , bgap.risk_model_code , bgap.model_version , bgap.payment_year - , bgap.recapture_flag + , bgap.recapturable_flag + , bgap.hcc_type + , bgap.hcc_source, , bgap.gap_status , bgap.hcc_hierarchy_group , bgap.hcc_hierarchy_group_rank , bgap.suspect_hcc_flag -- Apply hierarchies (i.e. if the hierarchy is not the min hierarchy, then remove it) - , case when bgap.hcc_hierarchy_group is not null and mhier.hcc_hierarchy_group is null then 1 else 0 end as filtered_out_by_hierarchy + , case when bgap.hcc_hierarchy_group is not null and mhier.hcc_hierarchy_group is null then 1 else 0 end as filtered_by_hierarchy_flag from best_gap_status as bgap -left outer join min_open_hierarchy as mhier - on bgap.person_id = mhier.person_id - and bgap.payer = mhier.payer - and bgap.payment_year = mhier.payment_year - and bgap.model_version = mhier.model_version - and bgap.hcc_hierarchy_group = mhier.hcc_hierarchy_group - and bgap.hcc_hierarchy_group_rank = mhier.min_hcc_hier_group_rank - and bgap.suspect_hcc_flag = mhier.suspect_hcc_flag --- Join eligible benes again here to capture new rows with open gaps -inner join {{ ref('hcc_recapture__stg_eligible_benes') }} as elig - on bgap.person_id = elig.person_id - and bgap.payment_year = elig.collection_year + 1 - and bgap.payer = elig.payer +left join min_open_hierarchy as mhier + on + bgap.person_id = mhier.person_id + and bgap.payer = mhier.payer + and bgap.payment_year = mhier.payment_year + and bgap.model_version = mhier.model_version + and bgap.hcc_hierarchy_group = mhier.hcc_hierarchy_group + and bgap.hcc_hierarchy_group_rank = mhier.min_hcc_hier_group_rank + -- Join eligible benes again here to capture new rows with open gaps +inner join {{ ref('hcc_recapture__int_eligible_benes') }} as elig + on + bgap.person_id = elig.person_id + and bgap.payment_year = elig.collection_year + 1 + and bgap.payer = elig.payer where 1 = (case when bgap.payment_year >= 2026 and bgap.model_version = 'CMS-HCC-V24' then 0 else 1 end) diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql new file mode 100644 index 000000000..df8b28f0e --- /dev/null +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql @@ -0,0 +1,105 @@ +{{ config( + enabled = var('claims_enabled', False) | as_bool + ) +}} + +WITH seed_hcc_hierarchy AS ( + SELECT + model_version + , hcc_code + , hcc_hierarchy_group + , hcc_hierarchy_group_rank + FROM {{ ref('hcc_recapture__stg_hierarchy') }} +), + +chronic_hccs AS ( + SELECT + mpgs.hcc_code + , mpgs.model_version + , CASE WHEN mpgs.acute_condition_flag = 'N' THEN 1 ELSE 0 END AS chronic_flag + , CASE WHEN mpgs.acute_condition_flag = 'Y' THEN 1 ELSE 0 END AS acute_flag + FROM {{ ref('homeward_chronic_conditions') }} AS mpgs +), + +get_risk_code AS ( + SELECT DISTINCT + person_id + , payer + , payment_year + , model_version + , risk_model_code + , ROW_NUMBER() OVER (PARTITION BY person_id, payment_year, model_version ORDER BY collection_end_date DESC) AS month_order + FROM {{ ref('cms_hcc__int_demographic_factors') }} + WHERE LOWER(factor_type) = 'demographic' +), + +medical_claims AS ( +-- Use distinct to remove claim line + SELECT DISTINCT + person_id + , payer + , claim_id + , rendering_npi + FROM {{ ref('core__medical_claim') }} +), + +eligible_hccs AS ( + SELECT * FROM {{ ref('hcc_recapture__int_coded_hccs') }} + + UNION ALL + + SELECT * FROM {{ ref('hcc_recapture__int_suspect_hccs') }} +) + +-- NOTE: Distinct is to remove different recording dates + ICD 10 codes for the same HCC code +SELECT DISTINCT + sus.person_id + , sus.payer + , sus.data_source + , {{ date_part('year', 'sus.recorded_date') }} AS collection_year + , sus.recorded_date + , sus.model_version + , sus.claim_id + , sus.hcc_code + , sus.hcc_description + , chronic.chronic_flag AS hcc_chronic_flag + , COALESCE(hier.hcc_hierarchy_group, 'no hierarchy') AS hcc_hierarchy_group + , COALESCE(hier.hcc_hierarchy_group_rank, 1) AS hcc_hierarchy_group_rank + , rcode.risk_model_code + , CASE WHEN elig_bene.person_id IS NOT NULL THEN 1 ELSE 0 END AS eligible_bene_flag + , eligible_claim_flag + , med.rendering_npi + , suspect_hcc_flag + , CASE WHEN chronic.chronic_flag = 1 AND eligible_claim_flag = 1 THEN 1 ELSE 0 END AS recapturable_flag + , hcc_type + , hcc_source +FROM eligible_hccs AS sus +LEFT JOIN seed_hcc_hierarchy AS hier + ON + sus.hcc_code = hier.hcc_code + AND sus.model_version = hier.model_version +LEFT JOIN chronic_hccs AS chronic + ON + sus.model_version = chronic.model_version + AND sus.hcc_code = chronic.hcc_code +LEFT JOIN get_risk_code AS rcode + ON + sus.person_id = rcode.person_id + AND sus.payer = rcode.payer + AND {{ date_part('year', 'sus.recorded_date') }} = rcode.payment_year - 1 + AND sus.model_version = rcode.model_version + AND rcode.month_order = 1 +LEFT JOIN medical_claims AS med + ON + sus.person_id = med.person_id + AND sus.payer = med.payer + AND sus.claim_id = med.claim_id +-- Only include benes eligible for gap closure +LEFT JOIN {{ ref('hcc_recapture__int_eligible_benes') }} AS elig_bene + ON + sus.person_id = elig_bene.person_id + AND {{ date_part('year', 'sus.recorded_date') }} = elig_bene.collection_year + AND sus.payer = elig_bene.payer +WHERE sus.hcc_code IS NOT NULL +-- Replace with cms_hcc__adjustment_rates once that table includes PY 2026 +AND 1 = (CASE WHEN {{ date_part('year', 'sus.recorded_date') }} >= 2025 AND sus.model_version = 'CMS-HCC-V24' THEN 0 ELSE 1 END) \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql new file mode 100644 index 000000000..a02668262 --- /dev/null +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql @@ -0,0 +1,34 @@ +with eligible_claims as ( +-- Use distinct to remove claim line + select distinct + person_id + , claim_id + , payer + from {{ ref('cms_hcc__int_eligible_conditions') }} +) + +select + cond.person_id + , cond.payer + , cond.data_source + , cond.recorded_date + , cond.model_version + , cond.claim_id + , cond.hcc_code + , cond.hcc_description + , 0 as suspect_hcc_flag + , case + when elig.claim_id is not null then 1 + else 0 + end as eligible_claim_flag + , 'coded' as hcc_type + , 'payer' as hcc_source +-- Not using list_all since it doesn't have claim_id pulled through +-- TODO: Update hcc_suspecting__list_all to have claim ID as well +from {{ ref('hcc_suspecting__int_all_conditions') }} as cond +left join eligible_claims as elig + on + cond.person_id = elig.person_id + and cond.payer = elig.payer + and cond.claim_id = elig.claim_id +where lower(condition_type) = 'discharge_diagnosis' \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql similarity index 73% rename from models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_hccs.sql rename to models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql index 3ecfc6eb1..ca910c565 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql @@ -1,15 +1,8 @@ -{{ config( - enabled = var('claims_enabled', False) | as_bool - ) -}} - with base as ( select * -from {{ ref('hcc_recapture__int_all_hccs') }} --- hierarchies should only be applied to eligible claims -where eligible_claim_indicator = 1 - and hcc_chronic_flag = 1 +from {{ ref('hcc_recapture__int_all_hccs')}} +where recapturable_flag = 1 ) , min_hierarchy as ( @@ -21,6 +14,7 @@ select , data_source , hcc_hierarchy_group , suspect_hcc_flag + , hcc_chronic_flag , min(hcc_hierarchy_group_rank) as min_hcc_hier_group_rank from base group by @@ -31,6 +25,7 @@ group by , data_source , hcc_hierarchy_group , suspect_hcc_flag + , hcc_chronic_flag ) select @@ -47,14 +42,15 @@ select , base.hcc_hierarchy_group , base.hcc_hierarchy_group_rank , base.risk_model_code - , base.eligible_bene - , base.eligible_claim_indicator + , base.eligible_bene_flag , base.rendering_npi - , base.reason - , base.condition_type , base.suspect_hcc_flag + , base.recapturable_flag + , base.hcc_type + , base.hcc_source + , CASE WHEN mhier.hcc_hierarchy_group IS NOT NULL THEN 0 ELSE 1 END as filtered_by_hierarchy_flag from base -left outer join min_hierarchy as mhier +left join min_hierarchy mhier on base.person_id = mhier.person_id and base.payer = mhier.payer and base.collection_year = mhier.collection_year @@ -63,5 +59,4 @@ left outer join min_hierarchy as mhier and base.hcc_hierarchy_group = mhier.hcc_hierarchy_group and base.hcc_hierarchy_group_rank = mhier.min_hcc_hier_group_rank and base.suspect_hcc_flag = mhier.suspect_hcc_flag --- Apply hierarchies -where mhier.hcc_hierarchy_group is not null + and base.hcc_chronic_flag = mhier.hcc_chronic_flag diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_suspect_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_suspect_hccs.sql new file mode 100644 index 000000000..ee3633fbe --- /dev/null +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_suspect_hccs.sql @@ -0,0 +1,28 @@ +{{ config( + enabled = var('claims_enabled', False) | as_bool + ) +}} + +select + person_id + , payer + , data_source + , suspect_date as recorded_date + , model_version + , null as claim_id + , hcc_code + , hcc_description + , 1 as suspect_hcc_flag + , 1 as eligible_claim_flag + , 'suspect' as hcc_type + , 'payer' as hcc_source +from {{ ref('hcc_suspecting__list_all') }} +-- Exclude since already included in int_all_conditions +where lower(reason) != 'prior coding history' + +{% if var('hcc_recapture_suspect_list') %} +union all +select + * +from {{ ref('hcc_recapture__stg_suspect_list')}} +{% endif %} \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_eligible_benes.sql b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_eligible_benes.sql index 3cb8f099a..54a67e866 100644 --- a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_eligible_benes.sql +++ b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_eligible_benes.sql @@ -4,9 +4,10 @@ }} -- Flattening months to 1 person per year -select distinct +select distinct person_id , {{ date_part('year', 'collection_end_date') }} as collection_year + , age_group , payer from {{ ref('cms_hcc__int_members') }} -- Don't support ESRD risk scores yet diff --git a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_suspect_hccs.sql b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_suspect_hccs.sql new file mode 100644 index 000000000..73e7df372 --- /dev/null +++ b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_suspect_hccs.sql @@ -0,0 +1,19 @@ +{{ config( + enabled = var('hcc_recapture_suspect_list') | as_bool + ) +}} + +select + person_id + , payer + , data_source + , recorded_date + , model_version + , claim_id + , hcc_code + , hcc_description + , suspect_hcc_flag + , eligible_claim_flag + , hcc_type + , hcc_source +from {{ ref('suspect_hccs')}} diff --git a/models/data_marts/hcc_recapture/staging_models.yml b/models/data_marts/hcc_recapture/staging_models.yml index 91664e7dd..aa7b5e8f7 100644 --- a/models/data_marts/hcc_recapture/staging_models.yml +++ b/models/data_marts/hcc_recapture/staging_models.yml @@ -65,3 +65,14 @@ models: description: The year that the claims originated and were originally coded. - name: payer description: The name of the person (i.e. beneficiary) insurance provider. + + - name: hcc_recapture__stg_suspect_hccs + description: | + A list of suspected HCCs. This is an optional model which can be included by + setting the hcc_recapture_suspect_list variable in your dbt_project.yml + config: + schema: | + {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture + {% else %}hcc_recapture{%- endif -%} + alias: stg_suspect_hccs + tags: ["hcc_recapture", "staging"] \ No newline at end of file From c9e4db6145e9fdb827544d3db7d781b64769362e Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 11:45:37 -0700 Subject: [PATCH 14/40] removing replaced model from yml --- models/data_marts/hcc_recapture/intermediate_models.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/models/data_marts/hcc_recapture/intermediate_models.yml b/models/data_marts/hcc_recapture/intermediate_models.yml index fe1dde6dd..a5b3de393 100644 --- a/models/data_marts/hcc_recapture/intermediate_models.yml +++ b/models/data_marts/hcc_recapture/intermediate_models.yml @@ -74,15 +74,6 @@ models: description: > Whether the claim is eligible for risk adjustment as determined based on a list of accepted CPT/HCPCs codes from CMS. These are available on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - - name: hcc_recapture__int_hccs - description: Filter to just eligible claims and apply HCC hierarchies. - config: - schema: | - {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture - {% else %}hcc_recapture{%- endif -%} - alias: int_hccs - tags: ["hcc_recapture", "intermediate"] - materialized: table tests: - dbt_utils.unique_combination_of_columns: combination_of_columns: From 608049b9d05d8bea7391c9beb7acd840a6ef0782 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 12:24:57 -0700 Subject: [PATCH 15/40] Replacing hierarchy with flat cms hcc terminology seed --- .../intermediate/hccs/hcc_recapture__int_all_hccs.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql index df8b28f0e..edc9c0242 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql @@ -9,7 +9,7 @@ WITH seed_hcc_hierarchy AS ( , hcc_code , hcc_hierarchy_group , hcc_hierarchy_group_rank - FROM {{ ref('hcc_recapture__stg_hierarchy') }} + FROM {{ ref('cms_hcc__disease_hierarchy_flat') }} ), chronic_hccs AS ( From 3b757a4af2cff6342b384d2198dd5df727de79ad Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 12:47:45 -0700 Subject: [PATCH 16/40] updating reference --- .../intermediate/hccs/hcc_recapture__int_suspect_hccs.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_suspect_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_suspect_hccs.sql index ee3633fbe..7a680213f 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_suspect_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_suspect_hccs.sql @@ -24,5 +24,5 @@ where lower(reason) != 'prior coding history' union all select * -from {{ ref('hcc_recapture__stg_suspect_list')}} +from {{ ref('hcc_recapture__stg_suspect_hccs')}} {% endif %} \ No newline at end of file From 5b9d3938cb74c577a5cb40422474f44475bda2e1 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 12:59:00 -0700 Subject: [PATCH 17/40] Adding additional intermediate models to yaml --- .../hcc_recapture/intermediate_models.yml | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/models/data_marts/hcc_recapture/intermediate_models.yml b/models/data_marts/hcc_recapture/intermediate_models.yml index a5b3de393..7134fcbe8 100644 --- a/models/data_marts/hcc_recapture/intermediate_models.yml +++ b/models/data_marts/hcc_recapture/intermediate_models.yml @@ -138,6 +138,7 @@ models: description: > Whether the claim is eligible for risk adjustment as determined based on a list of accepted CPT/HCPCs codes from CMS. These are available on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment + - name: hcc_recapture__int_gap_status description: > Determines the gap for a given HCC code. A gap is based on prior year claims which qualified as an HCC. If there was a chronic HCC in a prior year, @@ -207,3 +208,56 @@ models: description: > This is a 1 if this HCC gets filtered out due to a hierarchy being applied or not. The hierarchy is re-applied in this model due to interactions between open HCCs and closed HCCs originating in different collection years. + + - name: hcc_recapture__int_eligible_benes + description: > + config: + schema: | + {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture + {% else %}hcc_recapture{%- endif -%} + alias: int_eligible_benes + tags: ["hcc_recapture", "intermediate"] + materialized: table + + - name: hcc_recapture__int_determine_gap_status + description: > + config: + schema: | + {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture + {% else %}hcc_recapture{%- endif -%} + alias: int_determine_gap_status + tags: ["hcc_recapture", "intermediate"] + materialized: table + + - name: hcc_recapture__int_coded_hccs + description: > + config: + schema: | + {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture + {% else %}hcc_recapture{%- endif -%} + alias: int_coded_hccs + tags: ["hcc_recapture", "intermediate"] + materialized: table + + - name: hcc_recapture__int_recapturable_hccs + description: > + config: + schema: | + {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture + {% else %}hcc_recapture{%- endif -%} + alias: int_recapturable_hccs + tags: ["hcc_recapture", "intermediate"] + materialized: table + + - name: hcc_recapture__int_suspect_hccs + description: > + config: + schema: | + {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture + {% else %}hcc_recapture{%- endif -%} + alias: int_suspect_hccs + tags: ["hcc_recapture", "intermediate"] + materialized: table + + + \ No newline at end of file From 822c4b7fa8a5b544c890ee66eebdfcea3d8599fa Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 15:39:03 -0700 Subject: [PATCH 18/40] Updating yml files --- .../data_marts/hcc_recapture/final_models.yml | 99 +++++------ .../hcc_recapture/intermediate_models.yml | 159 +++++++++++++----- .../hcc_recapture/staging_models.yml | 20 ++- 3 files changed, 183 insertions(+), 95 deletions(-) diff --git a/models/data_marts/hcc_recapture/final_models.yml b/models/data_marts/hcc_recapture/final_models.yml index 2e35ada89..aa83b7dff 100644 --- a/models/data_marts/hcc_recapture/final_models.yml +++ b/models/data_marts/hcc_recapture/final_models.yml @@ -118,20 +118,23 @@ models: alias: hcc_status tags: ["hcc_recapture", "final"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: - - person_id - - payer - - data_source - - payment_year - - recorded_date - - claim_id - - hcc_code - - rendering_npi - - model_version - - hcc_hierarchy_group - - hcc_hierarchy_group_rank + arguments: + combination_of_columns: + - person_id + - payer + - data_source + - payment_year + - recorded_date + - claim_id + - hcc_code + - rendering_npi + - model_version + - hcc_hierarchy_group + - hcc_hierarchy_group_rank + - suspect_hcc_flag + - filtered_by_hierarchy_flag columns: - name: person_id description: A unique identifier for a person. @@ -141,8 +144,8 @@ models: description: The name of the data source origin. Filled in by the user in the input layer files. - name: payment_year description: > - This is the year that the HCC should be coded. Typically the collection year + 1, but for open HCCs it will be the collection year + 2. - To illustrate, if we have an HCC claim in 2023, but not in 2024, that means it is open in 2024. The payment year is 2024 + 1 = 2025. So + This is the year that the HCC should be coded. Typically the collection year + 1, but for open HCCs it will be the collection year + 2. + To illustrate, if we have an HCC claim in 2023, but not in 2024, that means it is open in 2024. The payment year is 2024 + 1 = 2025. So an open HCC from 2023 will have a payment year = 2025. - name: payment_year - name: recorded_date description: The date the claim was originally recorded. Based on admission date, but if null then filled in by claim start date followed by claim end date. @@ -152,9 +155,9 @@ models: description: The National Provider Identifier (NPI) of the physician who rendered services to the beneficiary. - name: model_version description: > - The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the - yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment - website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment + The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the + yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment + website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - name: hcc_code description: The hierarchical condition category code. - name: hcc_description @@ -165,21 +168,21 @@ models: For example, in 2025, the hierarchies were stored in a file called `V28115H1.txt`. The file will end in an H. - name: hcc_hierarchy_group_rank description: > - The rank within the HCC hierarchy group. The lower the number, the higher the rank. When 2 HCCs within the same group are + The rank within the HCC hierarchy group. The lower the number, the higher the rank. When 2 HCCs within the same group are coded within the same collection year, the HCC with the lower rank will be chosen of the two for a given beneficiary. - name: risk_model_code description: > There are different coefficients depending on a few factors such as dual eligibility, new enrollee, institutional status, etc... This column provides acronyms based off of the CMS risk adjustment SAS code. For example, these can be found in the `C2824T2N_25.csv` file for - the 2025 CMS risk adjustment software. - - name: eligible_claim_indicator + the 2025 CMS risk adjustment software. + - name: eligible_claim_flag description: > Whether the claim is eligible for risk adjustment as determined based on a list of accepted CPT/HCPCs codes from CMS. These are available on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - - name: eligible_bene + - name: eligible_bene_flag description: A 1 is indicated for a beneficiary who is in the eligibility files. A 0 is indicated if they are not in the eligibility files. - - name: reason - description: Free text reason for the appointment or service. + - name: hcc_chronic_flag + description: Whether the HCC is considered chronic or not. A 1 indicates the HCC is chronic. - name: gap_status description: > definitions for gap_status: @@ -188,12 +191,12 @@ models: - 'closed using lower coefficient hcc in hierarchy group': An HCC in the same group was closed, but its coefficient is less than the prior year HCC - 'new': defined as an hcc that has not been coded in the past 2 years - 'open': for gaps and claims, it's a chronic condition appropriate for recapture that has not been documented in current collection year - - 'inappropriate for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. - - name: recapture_flag - description: > + - 'ineligible for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. + - name: recapturable_flag + description: > Whether or not the HCC is recapturable. Includes the following values: - - 'Y': condition is a chronic condition appropriate for recapture and has been documented in the prior 2 years - - 'N': condition is not a chronic condition appropriate for recapture or has not been documented in the prior 2 years + - '1': condition is a chronic condition appropriate for recapture and has been documented in the prior 2 years + - '0': condition is not a chronic condition appropriate for recapture or has not been documented in the prior 2 years - name: hcc_recapture__gap_status description: The gap status for each HCC. config: @@ -203,15 +206,17 @@ models: alias: gap_status tags: ["hcc_recapture", "final"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: - - person_id - - hcc_code - - payer - - model_version - - payment_year - - suspect_hcc_flag + arguments: + combination_of_columns: + - person_id + - hcc_code + - payer + - model_version + - payment_year + - hcc_type + - hcc_source columns: - name: person_id description: A unique identifier for a person. @@ -226,25 +231,25 @@ models: the 2025 CMS risk adjustment software. - name: model_version description: > - The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the - yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment + The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the + yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment. - name: payment_year description: > - This is the year that the HCC should be coded. Typically the collection year + 1, but for open HCCs it will be the collection year + 2. - To illustrate, if we have an HCC claim in 2023, but not in 2024, that means it is open in 2024. The payment year is 2024 + 1 = 2025. So + This is the year that the HCC should be coded. Typically the collection year + 1, but for open HCCs it will be the collection year + 2. + To illustrate, if we have an HCC claim in 2023, but not in 2024, that means it is open in 2024. The payment year is 2024 + 1 = 2025. So an open HCC from 2023 will have a payment year = 2025. - - name: recapture_flag - description: > + - name: recapturable_flag + description: > Whether or not the HCC is recapturable. Includes the following values: - - 'Y': condition is a chronic condition appropriate for recapture and has been documented in the prior 2 years - - 'N': condition is not a chronic condition appropriate for recapture or has not been documented in the prior 2 years- name: recapture_flag + - '1': condition is a chronic condition appropriate for recapture and has been documented in the prior 2 years + - '0': condition is not a chronic condition appropriate for recapture or has not been documented in the prior 2 years - name: gap_status - desription: > + description: > definitions for gap_status: - 'closed using higher coefficient hcc in hierarchy group': An HCC in the same group was closed, but its coefficient is greater than the prior year HCC - 'closed': the specific HCC in question has been observed in a risk adjustable claim during the collection year. - 'closed using lower coefficient hcc in hierarchy group': An HCC in the same group was closed, but its coefficient is less than the prior year HCC - 'new': defined as an hcc that has not been coded in the past 2 years - 'open': for gaps and claims, it's a chronic condition appropriate for recapture that has not been documented in current collection year - - 'inappropriate for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. + - 'ineligible for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. diff --git a/models/data_marts/hcc_recapture/intermediate_models.yml b/models/data_marts/hcc_recapture/intermediate_models.yml index 7134fcbe8..81cb95c7c 100644 --- a/models/data_marts/hcc_recapture/intermediate_models.yml +++ b/models/data_marts/hcc_recapture/intermediate_models.yml @@ -13,18 +13,17 @@ models: tests: - dbt_utils.unique_combination_of_columns: combination_of_columns: - - person_id - - hcc_code - - data_source - - payer - - model_version - - collection_year - - hcc_hierarchy_group - - recorded_date - - rendering_npi - - claim_id - - condition_type - - reason + - person_id + - hcc_code + - data_source + - payer + - model_version + - collection_year + - hcc_hierarchy_group + - recorded_date + - rendering_npi + - claim_id + - suspect_hcc_flag columns: - name: person_id description: A unique identifier for a person. @@ -36,8 +35,8 @@ models: description: The date the claim was originally recorded. Based on admission date, but if null then filled in by claim start date followed by claim end date. - name: model_version description: > - The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the - yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment + The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the + yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - name: hcc_code description: The hierarchical condition category code. @@ -55,22 +54,18 @@ models: For example, in 2025, the hierarchies were stored in a file called `V28115H1.txt`. The file will end in an H. - name: hcc_hierarchy_group_rank description: > - The rank within the HCC hierarchy group. The lower the number, the higher the rank. When 2 HCCs within the same group are + The rank within the HCC hierarchy group. The lower the number, the higher the rank. When 2 HCCs within the same group are coded within the same collection year, the HCC with the lower rank will be chosen of the two for a given beneficiary. - name: risk_model_code description: > There are different coefficients depending on a few factors such as dual eligibility, new enrollee, institutional status, etc... This column provides acronyms based off of the CMS risk adjustment SAS code. For example, these can be found in the `C2824T2N_25.csv` file for the 2025 CMS risk adjustment software. - - name: eligible_bene + - name: eligible_bene_flag description: A 1 is indicated for a beneficiary who is in the eligibility files. A 0 is indicated if they are not in the eligibility files. - name: rendering_npi description: The National Provider Identifier (NPI) of the physician who rendered services to the beneficiary. - - name: reason - description: Free text reason for the appointment or service. - - name: condition_type - description: The type of condition. Claims are filled in with values such as `discharge_diagnosis`. - - name: eligible_claim_indicator + - name: eligible_claim_flag description: > Whether the claim is eligible for risk adjustment as determined based on a list of accepted CPT/HCPCs codes from CMS. These are available on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment @@ -150,16 +145,18 @@ models: alias: int_gap_status tags: ["hcc_recapture", "intermediate"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: - - person_id - - hcc_code - - payer - - model_version - - payment_year - - hcc_hierarchy_group - - suspect_hcc_flag + arguments: + combination_of_columns: + - person_id + - hcc_code + - payer + - model_version + - payment_year + - hcc_hierarchy_group + - hcc_type + - hcc_source columns: - name: person_id description: A unique identifier for a person. @@ -171,22 +168,22 @@ models: description: > There are different coefficients depending on a few factors such as dual eligibility, new enrollee, institutional status, etc... This column provides acronyms based off of the CMS risk adjustment SAS code. For example, these can be found in the `C2824T2N_25.csv` file for - the 2025 CMS risk adjustment software. + the 2025 CMS risk adjustment software. - name: model_version description: > The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment - website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment + website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - name: payment_year description: > This is the year that the HCC should be coded. Typically the collection year + 1, but for open HCCs it will be the collection year + 2. To illustrate, if we have an HCC claim in 2023, but not in 2024, that means it is open in 2024. The payment year is 2024 + 1 = 2025. So an open HCC from 2023 will have a payment year = 2025. - - name: recapture_flag - description: > + - name: recapturable_flag + description: > Whether or not the HCC is recapturable. Includes the following values: - - 'Y': condition is a chronic condition appropriate for recapture and has been documented in the prior 2 years - - 'N': condition is not a chronic condition appropriate for recapture or has not been documented in the prior 2 years + - '1': condition is a chronic condition appropriate for recapture and has been documented in the prior 2 years + - '0': condition is not a chronic condition appropriate for recapture or has not been documented in the prior 2 years - name: gap_status description: > definitions for gap_status: @@ -195,7 +192,7 @@ models: - 'closed using lower coefficient hcc in hierarchy group': An HCC in the same group was closed, but its coefficient is less than the prior year HCC - 'new': defined as an hcc that has not been coded in the past 2 years - 'open': for gaps and claims, it's a chronic condition appropriate for recapture that has not been documented in current collection year - - 'inappropriate for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. + - 'ineligible for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. - name: hcc_hierarchy_group description: > The name of the HCC hierarchy group. Determined based off of the CMS risk adjustment website model software. @@ -204,13 +201,15 @@ models: description: > The rank within the HCC hierarchy group. The lower the number, the higher the rank. When 2 HCCs within the same group are coded within the same collection year, the HCC with the lower rank will be chosen of the two for a given beneficiary. - - name: filtered_out_by_hierarchy + - name: filtered_by_hierarchy_flag description: > This is a 1 if this HCC gets filtered out due to a hierarchy being applied or not. The hierarchy is re-applied in this model due to interactions between open HCCs and closed HCCs originating in different collection years. - name: hcc_recapture__int_eligible_benes description: > + Beneficiaries eligible for HCC recapture. When ESRD risk scores are available in the Tuva project, + the filter removing ESRD beneficiaries will need to be removed. config: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture @@ -218,9 +217,23 @@ models: alias: int_eligible_benes tags: ["hcc_recapture", "intermediate"] materialized: table - + data_tests: + - dbt_utils.unique_combination_of_columns: + arguments: + combination_of_columns: + - person_id + - collection_year + - payer + columns: + - name: person_id + description: A unique identifier for a person. + - name: collection_year + description: The year that the claims originated and were originally coded. + - name: payer + description: The name of the person (i.e. beneficiary) insurance provider. + - name: hcc_recapture__int_determine_gap_status - description: > + description: Labelling HCCs with a gap status config: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture @@ -230,7 +243,7 @@ models: materialized: table - name: hcc_recapture__int_coded_hccs - description: > + description: HCCs that were found based off of claims information. config: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture @@ -240,7 +253,7 @@ models: materialized: table - name: hcc_recapture__int_recapturable_hccs - description: > + description: Filter to just eligible claims and apply HCC hierarchies. config: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture @@ -248,9 +261,69 @@ models: alias: int_recapturable_hccs tags: ["hcc_recapture", "intermediate"] materialized: table + data_tests: + - dbt_utils.unique_combination_of_columns: + arguments: + combination_of_columns: + - person_id + - hcc_code + - data_source + - payer + - model_version + - collection_year + - hcc_hierarchy_group + - recorded_date + - rendering_npi + - claim_id + - suspect_hcc_flag + columns: + - name: person_id + description: A unique identifier for a person. + - name: payer + description: The name of the person (i.e. beneficiary) insurance provider. + - name: collection_year + description: The year that the claims originated and were originally coded. + - name: recorded_date + description: The date the claim was originally recorded. Based on admission date, but if null then filled in by claim start date followed by claim end date. + - name: model_version + description: > + The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the + yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment + website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment + - name: hcc_code + description: The hierarchical condition category code. + - name: hcc_description + description: A description of the HCC code. + - name: data_source + description: The name of the data source origin. Filled in by the user in the input layer files. + - name: hcc_chronic_flag + description: Whether the HCC is considered chronic or not. A 1 indicates the HCC is chronic. + - name: claim_id + description: A unique identifier for the claim. + - name: hcc_hierarchy_group + description: > + The name of the HCC hierarchy group. Determined based off of the CMS risk adjustment website model software. + For example, in 2025, the hierarchies were stored in a file called `V28115H1.txt`. The file will end in an H. + - name: hcc_hierarchy_group_rank + description: > + The rank within the HCC hierarchy group. The lower the number, the higher the rank. When 2 HCCs within the same group are + coded within the same collection year, the HCC with the lower rank will be chosen of the two for a given beneficiary. + - name: risk_model_code + description: > + There are different coefficients depending on a few factors such as dual eligibility, new enrollee, institutional status, etc... + This column provides acronyms based off of the CMS risk adjustment SAS code. For example, these can be found in the `C2824T2N_25.csv` file for + the 2025 CMS risk adjustment software. + - name: eligible_bene_flag + description: A 1 is indicated for a beneficiary who is in the eligibility files. A 0 is indicated if they are not in the eligibility files. + - name: rendering_npi + description: The National Provider Identifier (NPI) of the physician who rendered services to the beneficiary. + - name: eligible_claim_flag + description: > + Whether the claim is eligible for risk adjustment as determined based on a list of accepted CPT/HCPCs codes from CMS. These are available + on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - name: hcc_recapture__int_suspect_hccs - description: > + description: Suspect HCCs which are identified from sources besides medical claims conditions. config: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture diff --git a/models/data_marts/hcc_recapture/staging_models.yml b/models/data_marts/hcc_recapture/staging_models.yml index aa7b5e8f7..bc912214f 100644 --- a/models/data_marts/hcc_recapture/staging_models.yml +++ b/models/data_marts/hcc_recapture/staging_models.yml @@ -27,7 +27,7 @@ models: yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - name: hcc_code - description: The hierarchical condition category code. + description: The hierarchical condition category code. - name: hcc_hierarchy_group description: > The name of the HCC hierarchy group. Determined based off of the CMS risk adjustment website model software. @@ -55,9 +55,10 @@ models: tests: - dbt_utils.unique_combination_of_columns: combination_of_columns: - - person_id - - collection_year - - payer + - person_id + - collection_year + - payer + - age_group columns: - name: person_id description: A unique identifier for a person. @@ -65,7 +66,16 @@ models: description: The year that the claims originated and were originally coded. - name: payer description: The name of the person (i.e. beneficiary) insurance provider. - + - name: age_group + tests: + - not_null: + description: > + If there is no age, it is likely because they are missing a birth date in the input layer. + Risk model codes needs not to be null since it's used as a join argument to get equivalent coefficients. + Members without age groups are excluded. + config: + severity: warn + - name: hcc_recapture__stg_suspect_hccs description: | A list of suspected HCCs. This is an optional model which can be included by From d2d7cba658dbde322ab07a9b51e32a41e99a2ed3 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 15:42:48 -0700 Subject: [PATCH 19/40] add tests --- ...check_claim_type_not_only_undetermined.sql | 23 ------ ...heck_medical_claim_eligibility_overlap.sql | 70 ------------------- ...eck_pharmacy_claim_eligibility_overlap.sql | 70 ------------------- tests/check_pmpm_paid_amount_threshold.sql | 23 ------ tests/test_complete_hccs.sql | 13 ++++ tests/test_hierarchies.sql | 23 ++++++ tests/test_null_gap_status.sql | 30 ++++++++ tests/test_recapture_rates.sql | 12 ++++ 8 files changed, 78 insertions(+), 186 deletions(-) delete mode 100644 tests/check_claim_type_not_only_undetermined.sql delete mode 100644 tests/check_medical_claim_eligibility_overlap.sql delete mode 100644 tests/check_pharmacy_claim_eligibility_overlap.sql delete mode 100644 tests/check_pmpm_paid_amount_threshold.sql create mode 100644 tests/test_complete_hccs.sql create mode 100644 tests/test_hierarchies.sql create mode 100644 tests/test_null_gap_status.sql create mode 100644 tests/test_recapture_rates.sql diff --git a/tests/check_claim_type_not_only_undetermined.sql b/tests/check_claim_type_not_only_undetermined.sql deleted file mode 100644 index 89aeec314..000000000 --- a/tests/check_claim_type_not_only_undetermined.sql +++ /dev/null @@ -1,23 +0,0 @@ -{{ config( - enabled = var('claims_enabled', False) - | as_bool, - tags = ['dqi', 'tuva_dqi_sev_2', 'dqi_service_categories', 'dqi_ccsr', 'dqi_cms_chronic_conditions', - 'dqi_tuva_chronic_conditions', 'dqi_cms_hccs', 'dqi_ed_classification', - 'dqi_financial_pmpm', 'dqi_quality_measures', 'dqi_readmission'], - severity = 'warn' - ) -}} - -with claim_type_cte as ( - select - data_source - , min(case when claim_type = 'undetermined' then 1 else 0 end) as has_only_undetermined_claim_types - , count(*) as count_records - from {{ ref('input_layer__medical_claim') }} - group by data_source -) - -select - * -from claim_type_cte -where has_only_undetermined_claim_types = 1 diff --git a/tests/check_medical_claim_eligibility_overlap.sql b/tests/check_medical_claim_eligibility_overlap.sql deleted file mode 100644 index c3aa4f081..000000000 --- a/tests/check_medical_claim_eligibility_overlap.sql +++ /dev/null @@ -1,70 +0,0 @@ -{{ config( - enabled = var('claims_enabled', False) - | as_bool, - tags = ['dqi', 'tuva_dqi_sev_2', 'dqi_service_categories', 'dqi_ccsr', 'dqi_cms_chronic_conditions', - 'dqi_tuva_chronic_conditions', 'dqi_cms_hccs', 'dqi_ed_classification', - 'dqi_financial_pmpm', 'dqi_quality_measures', 'dqi_readmission'], - severity = 'warn' - ) -}} - -with eligibility as ( - select distinct - person_id - , data_source - from {{ ref('input_layer__eligibility') }} -) - -, medical_claims as ( - select distinct - person_id - , data_source - from {{ ref('input_layer__medical_claim') }} -) - -, mc_records_check as ( - select - data_source - , count(*) as n_rows - from medical_claims - group by data_source -) - -, elig_records_check as ( - select - data_source - , count(*) as n_rows - from eligibility - group by data_source -) - -, overlap_check as ( - select - m.data_source - , count(e.person_id) as n_rows - from medical_claims as m - left outer join eligibility as e - on m.person_id = e.person_id - and m.data_source = e.data_source - group by m.data_source -) - -, final as ( - select - oc.data_source - , oc.n_rows as n_overlapping_records - , case when (mc.n_rows = 0 or mc.n_rows is null) then 1 else 0 end as is_mc_empty - , case when (ec.n_rows = 0 or ec.n_rows is null) then 1 else 0 end as is_elig_empty - from overlap_check as oc - left outer join mc_records_check as mc - on oc.data_source = mc.data_source - left outer join elig_records_check as ec - on oc.data_source = ec.data_source -) - -select - data_source - , n_overlapping_records -from final -where not (is_mc_empty = 1 or is_elig_empty = 1) -and n_overlapping_records = 0 diff --git a/tests/check_pharmacy_claim_eligibility_overlap.sql b/tests/check_pharmacy_claim_eligibility_overlap.sql deleted file mode 100644 index 1653a4339..000000000 --- a/tests/check_pharmacy_claim_eligibility_overlap.sql +++ /dev/null @@ -1,70 +0,0 @@ -{{ config( - enabled = var('claims_enabled', False) - | as_bool, - tags = ['dqi', 'tuva_dqi_sev_2', 'dqi_service_categories', 'dqi_ccsr', 'dqi_cms_chronic_conditions', - 'dqi_tuva_chronic_conditions', 'dqi_cms_hccs', 'dqi_ed_classification', - 'dqi_financial_pmpm', 'dqi_quality_measures', 'dqi_readmission'], - severity = 'warn' - ) -}} - -with eligibility as ( - select distinct - person_id - , data_source - from {{ ref('input_layer__eligibility') }} -) - -, pharmacy_claims as ( - select distinct - person_id - , data_source - from {{ ref('input_layer__pharmacy_claim') }} -) - -, pc_records_check as ( - select - data_source - , count(*) as n_rows - from pharmacy_claims - group by data_source -) - -, elig_records_check as ( - select - data_source - , count(*) as n_rows - from eligibility - group by data_source -) - -, overlap_check as ( - select - p.data_source - , count(e.person_id) as n_rows - from pharmacy_claims as p - left outer join eligibility as e - on p.person_id = e.person_id - and p.data_source = e.data_source - group by p.data_source -) - -, final as ( - select - oc.data_source - , oc.n_rows as n_overlapping_records - , case when (pc.n_rows = 0 or pc.n_rows is null) then 1 else 0 end as is_pc_empty - , case when (ec.n_rows = 0 or ec.n_rows is null) then 1 else 0 end as is_elig_empty - from overlap_check as oc - left outer join pc_records_check as pc - on oc.data_source = pc.data_source - left outer join elig_records_check as ec - on oc.data_source = ec.data_source -) - -select - data_source - , n_overlapping_records -from final -where not (is_pc_empty = 1 or is_elig_empty = 1) -and n_overlapping_records = 0 diff --git a/tests/check_pmpm_paid_amount_threshold.sql b/tests/check_pmpm_paid_amount_threshold.sql deleted file mode 100644 index 55bae3edf..000000000 --- a/tests/check_pmpm_paid_amount_threshold.sql +++ /dev/null @@ -1,23 +0,0 @@ -{{ config( - enabled = var('claims_enabled', False) - | as_bool, - tags = ['dqi', 'tuva_dqi_sev_5', 'dqi_service_categories', 'dqi_ccsr', 'dqi_cms_chronic_conditions', - 'dqi_tuva_chronic_conditions', 'dqi_cms_hccs', 'dqi_ed_classification', - 'dqi_financial_pmpm', 'dqi_quality_measures', 'dqi_readmission'], - severity = 'warn' - ) -}} - -select - payer - , {{ quote_column('plan') }} - , data_source - , count(year_month) as member_months - , avg(total_paid) as avg_paid_pmpm -from {{ ref('financial_pmpm__pmpm_prep') }} -group by - payer - , data_source - , {{ quote_column('plan') }} -having avg(total_paid) > 10000 -and count(year_month) >= 100 diff --git a/tests/test_complete_hccs.sql b/tests/test_complete_hccs.sql new file mode 100644 index 000000000..95654b767 --- /dev/null +++ b/tests/test_complete_hccs.sql @@ -0,0 +1,13 @@ +--- This test ensures all HCCs are still accounted for after determining the gap status +select distinct person_id, payer, collection_year, model_version, hcc_code +from {{ ref('ra_ops__int_recapturable_hccs')}} +where eligible_bene_flag = 1 + and hcc_type = 'coded' + and filtered_by_hierarchy_flag = 0 + +MINUS + +select distinct person_id, payer, payment_year - 1, model_version, hcc_code +-- Using the int gap status since hierarchies aren't applied yet +from {{ ref('ra_ops__int_gap_status')}} +where gap_status != 'open' \ No newline at end of file diff --git a/tests/test_hierarchies.sql b/tests/test_hierarchies.sql new file mode 100644 index 000000000..715198fa5 --- /dev/null +++ b/tests/test_hierarchies.sql @@ -0,0 +1,23 @@ +with base as ( +select distinct + person_id, + payer, + payment_year, + model_version, + hcc_hierarchy_group, + hcc_hierarchy_group_rank +from {{ ref('ra_ops__int_gap_status') }} +where hcc_hierarchy_group != 'no hierarchy' + and filtered_by_hierarchy_flag = 0 +) + +select + person_id, + payer, + payment_year, + model_version, + hcc_hierarchy_group, + count(*) +from base +group by all +having count(*) > 1 diff --git a/tests/test_null_gap_status.sql b/tests/test_null_gap_status.sql new file mode 100644 index 000000000..0e93f9d34 --- /dev/null +++ b/tests/test_null_gap_status.sql @@ -0,0 +1,30 @@ +/* This test checks to ensure that all null gap status are due to one of the following reasons: +- Not a chronic HCC +- Is a suspect HCC (only when it overlaps with an actual claim) +- Was not an eligible claim +- Was filtered out due to a hierarchy + +If this test fails, that means that the logic needs to be reviewed to find what other reasons +are causing ineligible recaptures and, if needed, adjust this test accordingly. +*/ +select stat.* +from {{ ref('ra_ops__hcc_status') }} as stat +left join {{ ref('ra_ops__int_gap_status') }} gaps + on stat.person_id = gaps.person_id + and stat.hcc_code = gaps.hcc_code + and stat.payer = gaps.payer + and stat.model_version = gaps.model_version + and stat.payment_year = gaps.payment_year + and stat.suspect_hcc_flag = gaps.suspect_hcc_flag +left join {{ ref('ra_ops__int_recapturable_hccs')}} ext + on stat.person_id = ext.person_id + and stat.payer = ext.payer + and stat.data_source = ext.data_source + and stat.payment_year = ext.collection_year + 1 + and stat.hcc_code = ext.hcc_code + and stat.suspect_hcc_flag = ext.suspect_hcc_flag +where stat.gap_status = 'ineligible for recapture' + and stat.hcc_chronic_flag = 1 + and stat.suspect_hcc_flag = 0 + and stat.eligible_claim_flag = 1 + and stat.filtered_by_hierarchy_flag = 0 \ No newline at end of file diff --git a/tests/test_recapture_rates.sql b/tests/test_recapture_rates.sql new file mode 100644 index 000000000..471ddb6b2 --- /dev/null +++ b/tests/test_recapture_rates.sql @@ -0,0 +1,12 @@ +-- Ensure the latest YTD value matches the recapture rates +select + ytd.payer + , ytd.payment_year +from {{ref('ra_ops__recapture_rates_monthly_ytd')}} ytd +left join {{ref('ra_ops__recapture_rates')}} recap + on ytd.payer = recap.payer + and ytd.payment_year = recap.payment_year + and ytd.ytd_recapture_rate = recap.recapture_rate +where + month(ytd.payment_year_month) = 12 + and recap.payer is null From ffe4c59b17162daae337845924ffb0dcc6f24e21 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 16:01:43 -0700 Subject: [PATCH 20/40] fixing formatting --- .../final/hcc_recapture__hcc_status.sql | 21 ++- .../data_marts/hcc_recapture/final_models.yml | 19 ++- ...cc_recapture__int_determine_gap_status.sql | 61 ++++---- .../hcc_recapture__int_gap_status.sql | 22 ++- .../hccs/hcc_recapture__int_all_hccs.sql | 143 ++++++++++-------- .../hccs/hcc_recapture__int_coded_hccs.sql | 7 +- .../hcc_recapture/intermediate_models.yml | 69 +-------- .../hcc_recapture/staging_models.yml | 13 +- 8 files changed, 155 insertions(+), 200 deletions(-) diff --git a/models/data_marts/hcc_recapture/final/hcc_recapture__hcc_status.sql b/models/data_marts/hcc_recapture/final/hcc_recapture__hcc_status.sql index 8ce06dac4..306bcc6c7 100644 --- a/models/data_marts/hcc_recapture/final/hcc_recapture__hcc_status.sql +++ b/models/data_marts/hcc_recapture/final/hcc_recapture__hcc_status.sql @@ -45,15 +45,14 @@ left join {{ ref('hcc_recapture__int_recapturable_hccs') }} as recap and hccs.hcc_hierarchy_group = recap.hcc_hierarchy_group and coalesce(hccs.claim_id, '') = coalesce(recap.claim_id, '') left join {{ ref('hcc_recapture__int_gap_status') }} as gap - on - hccs.person_id = gap.person_id - and hccs.payer = gap.payer - and hccs.model_version = gap.model_version - and hccs.hcc_code = gap.hcc_code - -- For TUVA gaps, +2 is needed because we’re comparing collection year to payment year - we already need a +1 for that comparison, and an additional +1 to account for closure in the following year. - -- For suspect HCCs, we only apply +1, since it’s ok for those HCCs to close themselves within the same year (e.g., CY 2025 suspect list HCCs can be closed in CY 2025 based on claims rather than CY 2026). - and (case - when gap.gap_status = 'open' and hccs.hcc_type = 'coded' then hccs.collection_year + 2 - else hccs.collection_year + 1 - end) = gap.payment_year + on hccs.person_id = gap.person_id + and hccs.payer = gap.payer + and hccs.model_version = gap.model_version + and hccs.hcc_code = gap.hcc_code + -- For TUVA gaps, +2 is needed because we’re comparing collection year to payment year - we already need a +1 for that comparison, and an additional +1 to account for closure in the following year. + -- For suspect HCCs, we only apply +1, since it’s ok for those HCCs to close themselves within the same year (e.g., CY 2025 suspect list HCCs can be closed in CY 2025 based on claims rather than CY 2026). + and (case + when gap.gap_status = 'open' and hccs.hcc_type = 'coded' then hccs.collection_year + 2 + else hccs.collection_year + 1 + end) = gap.payment_year where hccs.eligible_bene_flag = 1 \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/final_models.yml b/models/data_marts/hcc_recapture/final_models.yml index aa83b7dff..6c634df59 100644 --- a/models/data_marts/hcc_recapture/final_models.yml +++ b/models/data_marts/hcc_recapture/final_models.yml @@ -10,9 +10,10 @@ models: alias: recapture_rates tags: ["hcc_recapture", "final"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: + arguments: + combination_of_columns: - payer - payment_year columns: @@ -31,6 +32,7 @@ models: description: The total number of HCCs that were open or closed in the payment year. Excludes new and inappropriate for recapture HCCs. - name: recapture_rate description: Closed HCCs divided by Total HCCs in the given payment year. + - name: hcc_recapture__recapture_rates_monthly description: HCC recapture rates by payment year month. config: @@ -40,9 +42,10 @@ models: alias: recapture_rates_monthly tags: ["hcc_recapture", "final"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: + arguments: + combination_of_columns: - payer - payment_year - payment_year_month @@ -66,6 +69,7 @@ models: description: The total number of HCCs that were open or closed in the payment month. Excludes new and inappropriate for recapture HCCs. - name: recapture_rate description: Closed HCCs divided by Total HCCs in the given payment month. + - name: hcc_recapture__recapture_rates_monthly_ytd description: HCC recapture rates by payment month year-to-date. config: @@ -75,9 +79,10 @@ models: alias: recapture_rates_monthly_ytd tags: ["hcc_recapture", "final"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: + arguments: + combination_of_columns: - payer - payment_year - payment_year_month @@ -109,6 +114,7 @@ models: description: The number of HCCs open and closed in a payment year. - name: ytd_recapture_rate description: The running total of closed HCCs divided by total HCCs in a payment year. + - name: hcc_recapture__hcc_status description: Combines claims data with HCC gap status. config: @@ -197,6 +203,7 @@ models: Whether or not the HCC is recapturable. Includes the following values: - '1': condition is a chronic condition appropriate for recapture and has been documented in the prior 2 years - '0': condition is not a chronic condition appropriate for recapture or has not been documented in the prior 2 years + - name: hcc_recapture__gap_status description: The gap status for each HCC. config: diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql index 10ad013bd..5edae47f8 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql @@ -83,12 +83,11 @@ equiv_coef as ( , base.risk_model_code from {{ ref('hcc_recapture__stg_coef_hier') }} as base inner join {{ ref('hcc_recapture__stg_coef_hier') }} as self - on - base.hcc_hierarchy_group = self.hcc_hierarchy_group - and base.risk_model_code = self.risk_model_code - and base.coefficient = self.coefficient - and base.model_version = self.model_version - and base.hcc_code != self.hcc_code + on base.hcc_hierarchy_group = self.hcc_hierarchy_group + and base.risk_model_code = self.risk_model_code + and base.coefficient = self.coefficient + and base.model_version = self.model_version + and base.hcc_code != self.hcc_code ) -- Note: Gaps can only be closed using claims received from the payor or from Athena. @@ -126,36 +125,32 @@ select end as gap_status from filtered_hccs as base full outer join risk_gaps as gap - on - base.person_id = gap.person_id - and base.payer = gap.payer - and base.collection_year = gap.collection_year - and base.model_version = gap.model_version - and base.hcc_code = gap.hcc_code - -- Only coded or captured HCCs can close other HCCs - and base.hcc_type in ('coded', 'captured') + on base.person_id = gap.person_id + and base.payer = gap.payer + and base.collection_year = gap.collection_year + and base.model_version = gap.model_version + and base.hcc_code = gap.hcc_code + -- Only coded or captured HCCs can close other HCCs + and base.hcc_type in ('coded', 'captured') left join equiv_coef as equiv - on - base.model_version = equiv.model_version - and base.hcc_hierarchy_group = equiv.hcc_hierarchy_group - and base.hcc_code = equiv.hcc_code - and base.risk_model_code = equiv.risk_model_code + on base.model_version = equiv.model_version + and base.hcc_hierarchy_group = equiv.hcc_hierarchy_group + and base.hcc_code = equiv.hcc_code + and base.risk_model_code = equiv.risk_model_code left join best_past_rank as grp - on - base.person_id = grp.person_id - and base.payer = grp.payer - and base.collection_year = grp.collection_year - and base.model_version = grp.model_version - and base.hcc_hierarchy_group = grp.hcc_hierarchy_group - and grp.best_past_rank = grp.hcc_hierarchy_group_rank + on base.person_id = grp.person_id + and base.payer = grp.payer + and base.collection_year = grp.collection_year + and base.model_version = grp.model_version + and base.hcc_hierarchy_group = grp.hcc_hierarchy_group + and grp.best_past_rank = grp.hcc_hierarchy_group_rank left join best_current_rank as current_year_hier - on - gap.person_id = current_year_hier.person_id - and gap.payer = current_year_hier.payer - and gap.collection_year = current_year_hier.collection_year - and gap.model_version = current_year_hier.model_version - and gap.hcc_hierarchy_group = current_year_hier.hcc_hierarchy_group - and current_year_hier.best_current_rank = current_year_hier.hcc_hierarchy_group_rank + on gap.person_id = current_year_hier.person_id + and gap.payer = current_year_hier.payer + and gap.collection_year = current_year_hier.collection_year + and gap.model_version = current_year_hier.model_version + and gap.hcc_hierarchy_group = current_year_hier.hcc_hierarchy_group + and current_year_hier.best_current_rank = current_year_hier.hcc_hierarchy_group_rank -- Gaps are only eligible to be closed by claims data and the base table here is closing the gap aliased table -- The or hcc_type is null allows open HCCs to flow through where base.hcc_type in ('coded', 'captured') or base.hcc_type is null diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql index 5728cac2d..3154542cb 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql @@ -86,17 +86,15 @@ select distinct , case when bgap.hcc_hierarchy_group is not null and mhier.hcc_hierarchy_group is null then 1 else 0 end as filtered_by_hierarchy_flag from best_gap_status as bgap left join min_open_hierarchy as mhier - on - bgap.person_id = mhier.person_id - and bgap.payer = mhier.payer - and bgap.payment_year = mhier.payment_year - and bgap.model_version = mhier.model_version - and bgap.hcc_hierarchy_group = mhier.hcc_hierarchy_group - and bgap.hcc_hierarchy_group_rank = mhier.min_hcc_hier_group_rank - -- Join eligible benes again here to capture new rows with open gaps + on bgap.person_id = mhier.person_id + and bgap.payer = mhier.payer + and bgap.payment_year = mhier.payment_year + and bgap.model_version = mhier.model_version + and bgap.hcc_hierarchy_group = mhier.hcc_hierarchy_group + and bgap.hcc_hierarchy_group_rank = mhier.min_hcc_hier_group_rank + -- Join eligible benes again here to capture new rows with open gaps inner join {{ ref('hcc_recapture__int_eligible_benes') }} as elig - on - bgap.person_id = elig.person_id - and bgap.payment_year = elig.collection_year + 1 - and bgap.payer = elig.payer + on bgap.person_id = elig.person_id + and bgap.payment_year = elig.collection_year + 1 + and bgap.payer = elig.payer where 1 = (case when bgap.payment_year >= 2026 and bgap.model_version = 'CMS-HCC-V24' then 0 else 1 end) diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql index edc9c0242..0c33dd199 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql @@ -2,104 +2,121 @@ enabled = var('claims_enabled', False) | as_bool ) }} - -WITH seed_hcc_hierarchy AS ( - SELECT +with seed_hcc_hierarchy as ( + select model_version , hcc_code , hcc_hierarchy_group , hcc_hierarchy_group_rank - FROM {{ ref('cms_hcc__disease_hierarchy_flat') }} -), + from {{ ref('cms_hcc__disease_hierarchy_flat') }} +) + +, hcc_diagnosis as ( + select + payment_year + , diagnosis_code + , cms_hcc_v28 as hcc_code + , 'CMS-HCC-V28' as model_version + from {{ ref('cms_hcc__icd_10_cm_mappings') }} + where cms_hcc_v28_flag = 'Yes' -chronic_hccs AS ( - SELECT - mpgs.hcc_code - , mpgs.model_version - , CASE WHEN mpgs.acute_condition_flag = 'N' THEN 1 ELSE 0 END AS chronic_flag - , CASE WHEN mpgs.acute_condition_flag = 'Y' THEN 1 ELSE 0 END AS acute_flag - FROM {{ ref('homeward_chronic_conditions') }} AS mpgs -), + union all + + select + payment_year + , diagnosis_code + , cms_hcc_v24 as hcc_code + , 'CMS-HCC-V24' as model_version + from {{ ref('cms_hcc__icd_10_cm_mappings') }} + where cms_hcc_v24_flag = 'Yes' +) -get_risk_code AS ( - SELECT DISTINCT +, chronic_hccs as ( +select distinct + diag.hcc_code + , diag.payment_year + , diag.model_version + , 1 as chronic_flag +from {{ ref('chronic_conditions__cms_chronic_conditions_hierarchy') }} as hier +inner join hcc_diagnosis as diag + on hier.code = diag.diagnosis_code +) + +, get_risk_code as ( + select distinct person_id , payer , payment_year , model_version , risk_model_code - , ROW_NUMBER() OVER (PARTITION BY person_id, payment_year, model_version ORDER BY collection_end_date DESC) AS month_order - FROM {{ ref('cms_hcc__int_demographic_factors') }} - WHERE LOWER(factor_type) = 'demographic' -), + , row_number() over (partition by person_id, payment_year, model_version order by collection_end_date desc) as month_order + from {{ ref('cms_hcc__int_demographic_factors') }} + where lower(factor_type) = 'demographic' +) -medical_claims AS ( +, medical_claims as ( -- Use distinct to remove claim line - SELECT DISTINCT + select distinct person_id , payer , claim_id , rendering_npi - FROM {{ ref('core__medical_claim') }} -), + from {{ ref('core__medical_claim') }} +) -eligible_hccs AS ( - SELECT * FROM {{ ref('hcc_recapture__int_coded_hccs') }} +, eligible_hccs as ( + select * from {{ ref('hcc_recapture__int_coded_hccs') }} - UNION ALL + union all - SELECT * FROM {{ ref('hcc_recapture__int_suspect_hccs') }} + select * from {{ ref('hcc_recapture__int_suspect_hccs') }} ) -- NOTE: Distinct is to remove different recording dates + ICD 10 codes for the same HCC code -SELECT DISTINCT +select distinct sus.person_id , sus.payer , sus.data_source - , {{ date_part('year', 'sus.recorded_date') }} AS collection_year + , {{ date_part('year', 'sus.recorded_date') }} as collection_year , sus.recorded_date , sus.model_version , sus.claim_id , sus.hcc_code , sus.hcc_description - , chronic.chronic_flag AS hcc_chronic_flag - , COALESCE(hier.hcc_hierarchy_group, 'no hierarchy') AS hcc_hierarchy_group - , COALESCE(hier.hcc_hierarchy_group_rank, 1) AS hcc_hierarchy_group_rank + , chronic.chronic_flag as hcc_chronic_flag + , coalesce(hier.hcc_hierarchy_group, 'no hierarchy') as hcc_hierarchy_group + , coalesce(hier.hcc_hierarchy_group_rank, 1) as hcc_hierarchy_group_rank , rcode.risk_model_code - , CASE WHEN elig_bene.person_id IS NOT NULL THEN 1 ELSE 0 END AS eligible_bene_flag + , case when elig_bene.person_id is not null then 1 else 0 end as eligible_bene_flag , eligible_claim_flag , med.rendering_npi , suspect_hcc_flag - , CASE WHEN chronic.chronic_flag = 1 AND eligible_claim_flag = 1 THEN 1 ELSE 0 END AS recapturable_flag + , case when chronic.chronic_flag = 1 and eligible_claim_flag = 1 then 1 else 0 end as recapturable_flag , hcc_type , hcc_source -FROM eligible_hccs AS sus -LEFT JOIN seed_hcc_hierarchy AS hier - ON - sus.hcc_code = hier.hcc_code - AND sus.model_version = hier.model_version -LEFT JOIN chronic_hccs AS chronic - ON - sus.model_version = chronic.model_version - AND sus.hcc_code = chronic.hcc_code -LEFT JOIN get_risk_code AS rcode - ON - sus.person_id = rcode.person_id - AND sus.payer = rcode.payer - AND {{ date_part('year', 'sus.recorded_date') }} = rcode.payment_year - 1 - AND sus.model_version = rcode.model_version - AND rcode.month_order = 1 -LEFT JOIN medical_claims AS med - ON - sus.person_id = med.person_id - AND sus.payer = med.payer - AND sus.claim_id = med.claim_id +from eligible_hccs as sus +left join seed_hcc_hierarchy as hier + on sus.hcc_code = hier.hcc_code + and sus.model_version = hier.model_version +left join chronic_hccs as chronic + on sus.model_version = chronic.model_version + and sus.hcc_code = chronic.hcc_code + and {{ date_part('year', 'sus.recorded_date') }} = chronic.payment_year - 1 +left join get_risk_code as rcode + on sus.person_id = rcode.person_id + and sus.payer = rcode.payer + and {{ date_part('year', 'sus.recorded_date') }} = rcode.payment_year - 1 + and sus.model_version = rcode.model_version + and rcode.month_order = 1 +left join medical_claims as med + on sus.person_id = med.person_id + and sus.payer = med.payer + and sus.claim_id = med.claim_id -- Only include benes eligible for gap closure -LEFT JOIN {{ ref('hcc_recapture__int_eligible_benes') }} AS elig_bene - ON - sus.person_id = elig_bene.person_id - AND {{ date_part('year', 'sus.recorded_date') }} = elig_bene.collection_year - AND sus.payer = elig_bene.payer -WHERE sus.hcc_code IS NOT NULL --- Replace with cms_hcc__adjustment_rates once that table includes PY 2026 -AND 1 = (CASE WHEN {{ date_part('year', 'sus.recorded_date') }} >= 2025 AND sus.model_version = 'CMS-HCC-V24' THEN 0 ELSE 1 END) \ No newline at end of file +left join {{ ref('hcc_recapture__int_eligible_benes') }} as elig_bene + on sus.person_id = elig_bene.person_id + and {{ date_part('year', 'sus.recorded_date') }} = elig_bene.collection_year + and sus.payer = elig_bene.payer +where sus.hcc_code is not null + -- Replace with cms_hcc__adjustment_rates once that table includes PY 2026 + and 1 = (case when {{ date_part('year', 'sus.recorded_date') }} >= 2025 and sus.model_version = 'CMS-HCC-V24' then 0 else 1 end) \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql index a02668262..ae477d56c 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql @@ -27,8 +27,7 @@ select -- TODO: Update hcc_suspecting__list_all to have claim ID as well from {{ ref('hcc_suspecting__int_all_conditions') }} as cond left join eligible_claims as elig - on - cond.person_id = elig.person_id - and cond.payer = elig.payer - and cond.claim_id = elig.claim_id + on cond.person_id = elig.person_id + and cond.payer = elig.payer + and cond.claim_id = elig.claim_id where lower(condition_type) = 'discharge_diagnosis' \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/intermediate_models.yml b/models/data_marts/hcc_recapture/intermediate_models.yml index 81cb95c7c..b8ec0acfe 100644 --- a/models/data_marts/hcc_recapture/intermediate_models.yml +++ b/models/data_marts/hcc_recapture/intermediate_models.yml @@ -10,9 +10,10 @@ models: alias: int_all_hccs tags: ["hcc_recapture", "intermediate"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: + arguments: + combination_of_columns: - person_id - hcc_code - data_source @@ -69,70 +70,6 @@ models: description: > Whether the claim is eligible for risk adjustment as determined based on a list of accepted CPT/HCPCs codes from CMS. These are available on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - tests: - - dbt_utils.unique_combination_of_columns: - combination_of_columns: - - person_id - - hcc_code - - data_source - - payer - - model_version - - collection_year - - hcc_hierarchy_group - - recorded_date - - rendering_npi - - claim_id - - condition_type - - suspect_hcc_flag - columns: - - name: person_id - description: A unique identifier for a person. - - name: payer - description: The name of the person (i.e. beneficiary) insurance provider. - - name: collection_year - description: The year that the claims originated and were originally coded. - - name: recorded_date - description: The date the claim was originally recorded. Based on admission date, but if null then filled in by claim start date followed by claim end date. - - name: model_version - description: > - The CMS Medicare risk model version. This includes models such as v22,v24, and v28 which are documented in the - yearly rate announcement (e.g. https://www.cms.gov/files/document/2026-announcement.pdf) and on the CMS risk adjustment - website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - - name: hcc_code - description: The hierarchical condition category code. - - name: hcc_description - description: A description of the HCC code. - - name: data_source - description: The name of the data source origin. Filled in by the user in the input layer files. - - name: hcc_chronic_flag - description: Whether the HCC is considered chronic or not. A 1 indicates the HCC is chronic. - - name: claim_id - description: A unique identifier for the claim. - - name: hcc_hierarchy_group - description: > - The name of the HCC hierarchy group. Determined based off of the CMS risk adjustment website model software. - For example, in 2025, the hierarchies were stored in a file called `V28115H1.txt`. The file will end in an H. - - name: hcc_hierarchy_group_rank - description: > - The rank within the HCC hierarchy group. The lower the number, the higher the rank. When 2 HCCs within the same group are - coded within the same collection year, the HCC with the lower rank will be chosen of the two for a given beneficiary. - - name: risk_model_code - description: > - There are different coefficients depending on a few factors such as dual eligibility, new enrollee, institutional status, etc... - This column provides acronyms based off of the CMS risk adjustment SAS code. For example, these can be found in the `C2824T2N_25.csv` file for - the 2025 CMS risk adjustment software. - - name: eligible_bene - description: A 1 is indicated for a beneficiary who is in the eligibility files. A 0 is indicated if they are not in the eligibility files. - - name: rendering_npi - description: The National Provider Identifier (NPI) of the physician who rendered services to the beneficiary. - - name: reason - description: Free text reason for the appointment or service. - - name: condition_type - description: The type of condition. Claims are filled in with values such as `discharge_diagnosis`. - - name: eligible_claim_indicator - description: > - Whether the claim is eligible for risk adjustment as determined based on a list of accepted CPT/HCPCs codes from CMS. These are available - on the CMS risk adjustment website: https://www.cms.gov/medicare/payment/medicare-advantage-rates-statistics/risk-adjustment - name: hcc_recapture__int_gap_status description: > diff --git a/models/data_marts/hcc_recapture/staging_models.yml b/models/data_marts/hcc_recapture/staging_models.yml index bc912214f..11892e00c 100644 --- a/models/data_marts/hcc_recapture/staging_models.yml +++ b/models/data_marts/hcc_recapture/staging_models.yml @@ -12,9 +12,10 @@ models: alias: stg_coef_hier tags: ["hcc_recapture", "staging"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: + arguments: + combination_of_columns: - model_version - hcc_code - hcc_hierarchy_group @@ -41,6 +42,7 @@ models: There are different coefficients depending on a few factors such as dual eligibility, new enrollee, institutional status, etc... This column provides acronyms based off of the CMS risk adjustment SAS code. For example, these can be found in the `C2824T2N_25.csv` file for the 2025 CMS risk adjustment software. + - name: hcc_recapture__stg_eligible_benes description: > Beneficiaries eligible for HCC recapture. When ESRD risk scores are available in the Tuva project, @@ -52,9 +54,10 @@ models: alias: stg_eligible_benes tags: ["hcc_recapture", "staging"] materialized: table - tests: + data_tests: - dbt_utils.unique_combination_of_columns: - combination_of_columns: + arguments: + combination_of_columns: - person_id - collection_year - payer @@ -75,7 +78,7 @@ models: Members without age groups are excluded. config: severity: warn - + - name: hcc_recapture__stg_suspect_hccs description: | A list of suspected HCCs. This is an optional model which can be included by From ceaa3ca1240610bef7e925d8c8f9544f34c15d2a Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 16:14:24 -0700 Subject: [PATCH 21/40] removing addtl comma --- .../intermediate/hcc_recapture__int_gap_status.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql index 3154542cb..be4087590 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql @@ -77,7 +77,7 @@ select distinct , bgap.payment_year , bgap.recapturable_flag , bgap.hcc_type - , bgap.hcc_source, + , bgap.hcc_source , bgap.gap_status , bgap.hcc_hierarchy_group , bgap.hcc_hierarchy_group_rank From 9c3e3f53c43934bebbeeb441657631323f50aa07 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 16:21:58 -0700 Subject: [PATCH 22/40] Adding configs to some models --- .../intermediate/hcc_recapture__int_determine_gap_status.sql | 5 +++++ .../intermediate/hcc_recapture__int_eligible_benes.sql | 5 +++++ .../intermediate/hcc_recapture__int_gap_status.sql | 5 +++++ .../intermediate/hccs/hcc_recapture__int_coded_hccs.sql | 5 +++++ .../hccs/hcc_recapture__int_recapturable_hccs.sql | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql index 5edae47f8..c34e699c3 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_determine_gap_status.sql @@ -1,3 +1,8 @@ +{{ config( + enabled = var('claims_enabled', False) | as_bool + ) +}} + -- Get recapturable HCCs within the past 1 year with filtered_hccs as ( select * diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_eligible_benes.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_eligible_benes.sql index 6cd0504ae..904e18960 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_eligible_benes.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_eligible_benes.sql @@ -1,3 +1,8 @@ +{{ config( + enabled = var('claims_enabled', False) | as_bool + ) +}} + -- Flattening months to 1 person per year select distinct person_id diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql index be4087590..78f475a1b 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql @@ -1,3 +1,8 @@ +{{ config( + enabled = var('claims_enabled', False) | as_bool + ) +}} + -- Need to do this for HCCs in more than 1 group such as HCC 409 in v28 with add_rankings as ( select diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql index ae477d56c..e2775fce8 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_coded_hccs.sql @@ -1,3 +1,8 @@ +{{ config( + enabled = var('claims_enabled', False) | as_bool + ) +}} + with eligible_claims as ( -- Use distinct to remove claim line select distinct diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql index ca910c565..dab5e3b1b 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql @@ -1,3 +1,8 @@ +{{ config( + enabled = var('claims_enabled', False) | as_bool + ) +}} + with base as ( select * From 579be1e99fec744ba6ac71813c37e1cf19af3921 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 16:36:00 -0700 Subject: [PATCH 23/40] updating chronic hccs --- .../intermediate/hccs/hcc_recapture__int_all_hccs.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql index 0c33dd199..632c4f70a 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql @@ -34,12 +34,12 @@ with seed_hcc_hierarchy as ( , chronic_hccs as ( select distinct diag.hcc_code - , diag.payment_year , diag.model_version , 1 as chronic_flag from {{ ref('chronic_conditions__cms_chronic_conditions_hierarchy') }} as hier inner join hcc_diagnosis as diag on hier.code = diag.diagnosis_code +where diag.payment_year = (select max(payment_year) from hcc_diagnosis) ) , get_risk_code as ( From 5f6bfa9f9f8bdc76e99815e21911b010f7cbdc99 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 16:38:19 -0700 Subject: [PATCH 24/40] removing join on payment year --- .../intermediate/hccs/hcc_recapture__int_all_hccs.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql index 632c4f70a..10b3c63ec 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql @@ -100,8 +100,7 @@ left join seed_hcc_hierarchy as hier and sus.model_version = hier.model_version left join chronic_hccs as chronic on sus.model_version = chronic.model_version - and sus.hcc_code = chronic.hcc_code - and {{ date_part('year', 'sus.recorded_date') }} = chronic.payment_year - 1 + and sus.hcc_code = chronic.hcc_code left join get_risk_code as rcode on sus.person_id = rcode.person_id and sus.payer = rcode.payer From 6cd5dbbcf60680506b6f0cfa02e9336bf9706241 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 16:44:10 -0700 Subject: [PATCH 25/40] move chronic hccs --- .../hccs/hcc_recapture__int_all_hccs.sql | 31 ------------ .../hcc_recapture__stg_chronic_hccs.sql | 49 +++++++++++++++++++ .../hcc_recapture/staging_models.yml | 14 +++++- 3 files changed, 62 insertions(+), 32 deletions(-) create mode 100644 models/data_marts/hcc_recapture/staging/hcc_recapture__stg_chronic_hccs.sql diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql index 10b3c63ec..75271ff53 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql @@ -11,37 +11,6 @@ with seed_hcc_hierarchy as ( from {{ ref('cms_hcc__disease_hierarchy_flat') }} ) -, hcc_diagnosis as ( - select - payment_year - , diagnosis_code - , cms_hcc_v28 as hcc_code - , 'CMS-HCC-V28' as model_version - from {{ ref('cms_hcc__icd_10_cm_mappings') }} - where cms_hcc_v28_flag = 'Yes' - - union all - - select - payment_year - , diagnosis_code - , cms_hcc_v24 as hcc_code - , 'CMS-HCC-V24' as model_version - from {{ ref('cms_hcc__icd_10_cm_mappings') }} - where cms_hcc_v24_flag = 'Yes' -) - -, chronic_hccs as ( -select distinct - diag.hcc_code - , diag.model_version - , 1 as chronic_flag -from {{ ref('chronic_conditions__cms_chronic_conditions_hierarchy') }} as hier -inner join hcc_diagnosis as diag - on hier.code = diag.diagnosis_code -where diag.payment_year = (select max(payment_year) from hcc_diagnosis) -) - , get_risk_code as ( select distinct person_id diff --git a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_chronic_hccs.sql b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_chronic_hccs.sql new file mode 100644 index 000000000..0ff820f68 --- /dev/null +++ b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_chronic_hccs.sql @@ -0,0 +1,49 @@ + +{% if var('hcc_recapture_chronic_hccs') %} + +select + hcc_code + , model_version + , chronic_flag +from {{ ref('chronic_hccs') }} + +{% else %} + +with hcc_diagnosis as ( + select + payment_year + , diagnosis_code + , cms_hcc_v28 as hcc_code + , 'CMS-HCC-V28' as model_version + from {{ ref('cms_hcc__icd_10_cm_mappings') }} + where cms_hcc_v28_flag = 'Yes' + + union all + + select + payment_year + , diagnosis_code + , cms_hcc_v24 as hcc_code + , 'CMS-HCC-V24' as model_version + from {{ ref('cms_hcc__icd_10_cm_mappings') }} + where cms_hcc_v24_flag = 'Yes' +) + +, chronic_hccs as ( +select distinct + diag.hcc_code + , diag.model_version + , 1 as chronic_flag +from {{ ref('chronic_conditions__cms_chronic_conditions_hierarchy') }} as hier +inner join hcc_diagnosis as diag + on hier.code = diag.diagnosis_code +where diag.payment_year = (select max(payment_year) from hcc_diagnosis) +) + +select + hcc_code + , model_version + , chronic_flag +from chronic_hccs + +{% endif %} \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/staging_models.yml b/models/data_marts/hcc_recapture/staging_models.yml index 11892e00c..3d8a4f18d 100644 --- a/models/data_marts/hcc_recapture/staging_models.yml +++ b/models/data_marts/hcc_recapture/staging_models.yml @@ -88,4 +88,16 @@ models: {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture {% else %}hcc_recapture{%- endif -%} alias: stg_suspect_hccs - tags: ["hcc_recapture", "staging"] \ No newline at end of file + tags: ["hcc_recapture", "staging"] + + - name: hcc_recapture__stg_chronic_hccs + description: | + A list of suspected HCCs. This is an optional model which can be included by + setting the hcc_recapture_suspect_list variable in your dbt_project.yml + config: + schema: | + {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_hcc_recapture + {% else %}hcc_recapture{%- endif -%} + alias: stg_chronic_hccs + tags: ["hcc_recapture", "staging"] + \ No newline at end of file From cab21d66bb6a24cc1665e554055f4f7700ea5966 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Tue, 21 Apr 2026 16:44:59 -0700 Subject: [PATCH 26/40] Adding new chronic model --- .../intermediate/hccs/hcc_recapture__int_all_hccs.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql index 75271ff53..e3c03e414 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_all_hccs.sql @@ -67,7 +67,7 @@ from eligible_hccs as sus left join seed_hcc_hierarchy as hier on sus.hcc_code = hier.hcc_code and sus.model_version = hier.model_version -left join chronic_hccs as chronic +left join {{ ref('hcc_recapture__stg_chronic_hccs') }} as chronic on sus.model_version = chronic.model_version and sus.hcc_code = chronic.hcc_code left join get_risk_code as rcode From d46fe20b2a93a72d4deac246c3442dab72dd4a2b Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 08:21:54 -0700 Subject: [PATCH 27/40] removing encounter related updates --- .../acute_inpatient/acute_inpatient__generate_encounter_id.sql | 2 +- .../emergency_department__generate_encounter_id_pre_sort.sql | 2 +- .../inpatient_hospice__generate_encounter_id.sql | 2 +- .../inpatient_long_term__generate_encounter_id.sql | 2 +- .../inpatient_psych/inpatient_psych__generate_encounter_id.sql | 2 +- .../inpatient_rehab/inpatient_rehab__generate_encounter_id.sql | 2 +- .../inpatient_snf/inpatient_snf__generate_encounter_id.sql | 2 +- .../inpatient_substance_use__generate_encounter_id.sql | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql index b8f1fa1e0..c4eb2c482 100644 --- a/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/acute_inpatient/acute_inpatient__generate_encounter_id.sql @@ -38,7 +38,7 @@ select claim_id , end_date , discharge_disposition_code , facility_npi - , rank() over (partition by patient_data_source_id + , row_number() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql index a6067d324..c9ab5ecbc 100644 --- a/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql +++ b/models/claims_preprocessing/encounters/intermediate/emergency_department/emergency_department__generate_encounter_id_pre_sort.sql @@ -39,7 +39,7 @@ with claim_start_end as ( , facility_npi , claim_type , case when claim_type = 'professional' then 1 else 0 end as is_professional - , rank() over ( + , row_number() over ( partition by patient_data_source_id order by end_date, start_date, claim_id ) as row_num diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql index 8bd92a8c9..f78ebc8a6 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_hospice/inpatient_hospice__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_npi - , rank() over (partition by patient_data_source_id + , row_number() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql index 1f1c021a6..d5c0a0be7 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_long_term/inpatient_long_term__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_npi - , rank() over (partition by patient_data_source_id + , row_number() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql index 68412aceb..0132fc70b 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_psych/inpatient_psych__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_npi - , rank() over (partition by patient_data_source_id + , row_number() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql index 29b82d31f..10a1d3057 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_rehab/inpatient_rehab__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_npi - , rank() over (partition by patient_data_source_id + , row_number() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql index d0b1724b0..e1b92d071 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_snf/inpatient_snf__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_npi - , rank() over (partition by patient_data_source_id + , row_number() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) diff --git a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql index 23b24440f..39840d096 100644 --- a/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql +++ b/models/claims_preprocessing/encounters/intermediate/inpatient_substance_use/inpatient_substance_use__generate_encounter_id.sql @@ -38,7 +38,7 @@ with claim_start_end as ( , end_date , discharge_disposition_code , facility_npi - , rank() over (partition by patient_data_source_id + , row_number() over (partition by patient_data_source_id order by end_date, start_date, claim_id) as row_num from base ) From 82ebab7919bdc4d04ce8af8ad122b704ced58b96 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 08:31:37 -0700 Subject: [PATCH 28/40] small updates --- .../claims_preprocessing_models.yml | 2 +- ...hcc_recapture__recapture_rates_monthly.sql | 10 ++++-- .../hcc_recapture__int_recapturable_hccs.sql | 2 +- .../hcc_recapture__stg_eligible_benes.sql | 2 +- ...lized_input__int_medical_npi_normalize.sql | 33 +------------------ 5 files changed, 12 insertions(+), 37 deletions(-) diff --git a/models/claims_preprocessing/claims_preprocessing_models.yml b/models/claims_preprocessing/claims_preprocessing_models.yml index 9c8340503..25a1a0ac0 100644 --- a/models/claims_preprocessing/claims_preprocessing_models.yml +++ b/models/claims_preprocessing/claims_preprocessing_models.yml @@ -246,7 +246,7 @@ models: combination_of_columns: - claim_id - claim_line_number - - data_source + - data_source columns: - name: claim_id description: Unique identifier for each claim. diff --git a/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql b/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql index e29af7598..7f1e76716 100644 --- a/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql +++ b/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql @@ -9,7 +9,13 @@ select distinct person_id , payer , payment_year - , date_from_parts(payment_year, month(recorded_date),1) as payment_year_month + , cast({{ concat_custom([ + "payment_year" + , "'-'" + , date_part('month', 'recorded_date') + , "'-'" + , "'1'" + ]) }} as date) as payment_year_month , recorded_date , model_version , hcc_code @@ -24,7 +30,7 @@ where 1=1 ) , monthly_hcc_counts as ( -select +select payer , payment_year , payment_year_month diff --git a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql index dab5e3b1b..f81f9480e 100644 --- a/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql +++ b/models/data_marts/hcc_recapture/intermediate/hccs/hcc_recapture__int_recapturable_hccs.sql @@ -55,7 +55,7 @@ select , base.hcc_source , CASE WHEN mhier.hcc_hierarchy_group IS NOT NULL THEN 0 ELSE 1 END as filtered_by_hierarchy_flag from base -left join min_hierarchy mhier +left join min_hierarchy as mhier on base.person_id = mhier.person_id and base.payer = mhier.payer and base.collection_year = mhier.collection_year diff --git a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_eligible_benes.sql b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_eligible_benes.sql index 54a67e866..20d4cab96 100644 --- a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_eligible_benes.sql +++ b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_eligible_benes.sql @@ -4,7 +4,7 @@ }} -- Flattening months to 1 person per year -select distinct +select distinct person_id , {{ date_part('year', 'collection_end_date') }} as collection_year , age_group diff --git a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql index fe13855de..110eaf0da 100644 --- a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql +++ b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql @@ -4,7 +4,6 @@ ) }} -with base as ( select distinct med.claim_id , med.claim_line_number @@ -40,34 +39,4 @@ left outer join {{ ref('provider_data__provider') }} as bill_prov left outer join {{ ref('provider_data__provider') }} as fac_prov on med.facility_npi = fac_prov.npi and fac_prov.entity_type_description = 'Organization' - and med.claim_type = 'institutional' -) - -, facility_npi_ranked as ( -select - data_source - , claim_id - , normalized_facility_npi - , normalized_facility_name - , row_number() over (partition by data_source, claim_id order by claim_line_number) as facility_npi_rank -from base -where normalized_facility_npi is not null -) - -select - base.claim_id - , base.claim_line_number - , base.claim_type - , base.data_source - , base.normalized_rendering_npi - , base.normalized_rendering_name - , base.normalized_billing_npi - , base.normalized_billing_name - , fac.normalized_facility_npi - , fac.normalized_facility_name - , base.tuva_last_run -from base -left join facility_npi_ranked as fac - on base.claim_id = fac.claim_id - and base.data_source = fac.data_source - and facility_npi_rank = 1 \ No newline at end of file + and med.claim_type = 'institutional' \ No newline at end of file From 6cc85ec1b5043251a4e3e089c2552953b12bc5cf Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 08:37:55 -0700 Subject: [PATCH 29/40] update tests --- tests/test_complete_hccs.sql | 4 ++-- tests/test_hierarchies.sql | 2 +- tests/test_null_gap_status.sql | 6 +++--- tests/test_recapture_rates.sql | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_complete_hccs.sql b/tests/test_complete_hccs.sql index 95654b767..cd1dfe56f 100644 --- a/tests/test_complete_hccs.sql +++ b/tests/test_complete_hccs.sql @@ -1,6 +1,6 @@ --- This test ensures all HCCs are still accounted for after determining the gap status select distinct person_id, payer, collection_year, model_version, hcc_code -from {{ ref('ra_ops__int_recapturable_hccs')}} +from {{ ref('hcc_recapture__int_recapturable_hccs')}} where eligible_bene_flag = 1 and hcc_type = 'coded' and filtered_by_hierarchy_flag = 0 @@ -9,5 +9,5 @@ MINUS select distinct person_id, payer, payment_year - 1, model_version, hcc_code -- Using the int gap status since hierarchies aren't applied yet -from {{ ref('ra_ops__int_gap_status')}} +from {{ ref('hcc_recapture__int_gap_status')}} where gap_status != 'open' \ No newline at end of file diff --git a/tests/test_hierarchies.sql b/tests/test_hierarchies.sql index 715198fa5..68b4ee8a2 100644 --- a/tests/test_hierarchies.sql +++ b/tests/test_hierarchies.sql @@ -6,7 +6,7 @@ select distinct model_version, hcc_hierarchy_group, hcc_hierarchy_group_rank -from {{ ref('ra_ops__int_gap_status') }} +from {{ ref('hcc_recapture__int_gap_status') }} where hcc_hierarchy_group != 'no hierarchy' and filtered_by_hierarchy_flag = 0 ) diff --git a/tests/test_null_gap_status.sql b/tests/test_null_gap_status.sql index 0e93f9d34..39c58e2ba 100644 --- a/tests/test_null_gap_status.sql +++ b/tests/test_null_gap_status.sql @@ -8,15 +8,15 @@ If this test fails, that means that the logic needs to be reviewed to find what are causing ineligible recaptures and, if needed, adjust this test accordingly. */ select stat.* -from {{ ref('ra_ops__hcc_status') }} as stat -left join {{ ref('ra_ops__int_gap_status') }} gaps +from {{ ref('hcc_recapture__hcc_status') }} as stat +left join {{ ref('hcc_recapture__int_gap_status') }} gaps on stat.person_id = gaps.person_id and stat.hcc_code = gaps.hcc_code and stat.payer = gaps.payer and stat.model_version = gaps.model_version and stat.payment_year = gaps.payment_year and stat.suspect_hcc_flag = gaps.suspect_hcc_flag -left join {{ ref('ra_ops__int_recapturable_hccs')}} ext +left join {{ ref('hcc_recapture__int_recapturable_hccs')}} ext on stat.person_id = ext.person_id and stat.payer = ext.payer and stat.data_source = ext.data_source diff --git a/tests/test_recapture_rates.sql b/tests/test_recapture_rates.sql index 471ddb6b2..a9653e993 100644 --- a/tests/test_recapture_rates.sql +++ b/tests/test_recapture_rates.sql @@ -2,8 +2,8 @@ select ytd.payer , ytd.payment_year -from {{ref('ra_ops__recapture_rates_monthly_ytd')}} ytd -left join {{ref('ra_ops__recapture_rates')}} recap +from {{ref('hcc_recapture__recapture_rates_monthly_ytd')}} ytd +left join {{ref('hcc_recapture__recapture_rates')}} recap on ytd.payer = recap.payer and ytd.payment_year = recap.payment_year and ytd.ytd_recapture_rate = recap.recapture_rate From 2d785e0d8b9e0ff50467045267e374ddde4052a7 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 08:46:26 -0700 Subject: [PATCH 30/40] update tags --- models/data_marts/cms_hcc/cms_hcc_models.yml | 2 +- seeds/value_sets/cms_hcc/cms_hcc_seeds.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/models/data_marts/cms_hcc/cms_hcc_models.yml b/models/data_marts/cms_hcc/cms_hcc_models.yml index 53d6aaee2..1685bfbc2 100644 --- a/models/data_marts/cms_hcc/cms_hcc_models.yml +++ b/models/data_marts/cms_hcc/cms_hcc_models.yml @@ -258,7 +258,7 @@ models: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_cms_hcc{% else %}cms_hcc{%- endif -%} alias: _int_demographic_factors - tags: cms_hcc + tags: ["cms_hcc", "hcc_recapture"] materialized: table description: > Demographic and enrollment risk relative factor values for the corresponding HCC model version and payment year. diff --git a/seeds/value_sets/cms_hcc/cms_hcc_seeds.yml b/seeds/value_sets/cms_hcc/cms_hcc_seeds.yml index 1477a41df..5d0eae2e6 100644 --- a/seeds/value_sets/cms_hcc/cms_hcc_seeds.yml +++ b/seeds/value_sets/cms_hcc/cms_hcc_seeds.yml @@ -161,7 +161,7 @@ seeds: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_value_sets{% else %}value_sets{%- endif -%} alias: _value_set_disease_factors - tags: cms_hcc + tags: ["cms_hcc", "hcc_recapture"] enabled: "{{ var('claims_enabled', False) }}" column_types: model_version: | @@ -243,7 +243,7 @@ seeds: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_value_sets{% else %}value_sets{%- endif -%} alias: _value_set_disease_hierarchy_flat - tags: cms_hcc + tags: ["cms_hcc", "hcc_recapture"] enabled: "{{ var('claims_enabled', False) }}" column_types: hcc_code: | @@ -386,7 +386,7 @@ seeds: schema: | {%- if var('tuva_schema_prefix',None) != None -%}{{var('tuva_schema_prefix')}}_value_sets{% else %}value_sets{%- endif -%} alias: _value_set_cms_hcc_icd_10_cm_mappings - tags: cms_hcc + tags: ["cms_hcc", "hcc_recapture"] enabled: "{{ var('claims_enabled', False) }}" column_types: payment_year: | From c4ba063a39af3d1e2f161f4c2834806f7eed4ad2 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 09:14:31 -0700 Subject: [PATCH 31/40] replace qualify statements --- .../hcc_recapture__int_gap_status.sql | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql index 78f475a1b..b43ea01b1 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql @@ -38,20 +38,27 @@ with add_rankings as ( -- 2. If tie → prefer recapture (suspect_hcc_flag = 0) , best_hcc_type as ( select * - from add_rankings - qualify row_number() over ( + , row_number() over ( partition by person_id, payer, hcc_code, model_version, payment_year order by hcc_type_rank asc, suspect_hcc_flag asc -- 0 preferred over 1 - ) = 1 + ) as hcc_type_rank + from add_rankings ) -- Pick the best gap status , best_gap_status as ( - select * - from best_hcc_type - qualify min(gap_status_rank) over (partition by person_id, payer, hcc_code, model_version, payment_year) = gap_status_rank + select + * + from ( + select + * + , min(gap_status_rank) over (partition by person_id, payer, hcc_code, model_version, payment_year) as min_gap_status_rank + from best_hcc_type + where hcc_type_rank = 1 + ) + where min_gap_status_rank = gap_status_rank ) -- Find the minimum hierarchy for open hccs From c8fcd270d66d7c8195ae1f1287a2c9865fb2633d Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 09:18:27 -0700 Subject: [PATCH 32/40] updating naming --- .../intermediate/hcc_recapture__int_gap_status.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql index b43ea01b1..eeb1c47ec 100644 --- a/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql +++ b/models/data_marts/hcc_recapture/intermediate/hcc_recapture__int_gap_status.sql @@ -43,7 +43,7 @@ with add_rankings as ( order by hcc_type_rank asc, suspect_hcc_flag asc -- 0 preferred over 1 - ) as hcc_type_rank + ) as best_rank from add_rankings ) @@ -56,7 +56,7 @@ with add_rankings as ( * , min(gap_status_rank) over (partition by person_id, payer, hcc_code, model_version, payment_year) as min_gap_status_rank from best_hcc_type - where hcc_type_rank = 1 + where best_rank = 1 ) where min_gap_status_rank = gap_status_rank ) From 9f432d44c8ccb52a9f34cbb9eb8f0a16ec0ea55f Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 09:24:06 -0700 Subject: [PATCH 33/40] updating test --- tests/test_complete_hccs.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_complete_hccs.sql b/tests/test_complete_hccs.sql index cd1dfe56f..2d69b9577 100644 --- a/tests/test_complete_hccs.sql +++ b/tests/test_complete_hccs.sql @@ -5,7 +5,7 @@ where eligible_bene_flag = 1 and hcc_type = 'coded' and filtered_by_hierarchy_flag = 0 -MINUS +{{ dbt.except() }} select distinct person_id, payer, payment_year - 1, model_version, hcc_code -- Using the int gap status since hierarchies aren't applied yet From f397ff13f0fe215af194aacf8d9b74e43e83341c Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 09:41:16 -0700 Subject: [PATCH 34/40] updating docs --- docs/docs/data-marts/hcc-recapture.md | 2 +- .../data_marts/hcc_recapture/final_models.yml | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/docs/data-marts/hcc-recapture.md b/docs/docs/data-marts/hcc-recapture.md index 95f85af9c..8a5d8f8e7 100644 --- a/docs/docs/data-marts/hcc-recapture.md +++ b/docs/docs/data-marts/hcc-recapture.md @@ -30,7 +30,7 @@ The type of gap closure if provided using the `gap_status` field. Here are the g | closed using lower coefficient hcc in hierarchy group | An HCC in the same group was closed, but its coefficient is less than the prior year HCC | | new | Defined as an HCC that has not been coded in the past 2 years | | open | For gaps and claims, it's a chronic condition appropriate for recapture that has not been documented in current collection year | -| inappropriate for recapture | The specific HCC in question is "Open" and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it's not a chronic diagnosis | +| ineligible for recapture | The specific HCC in question is "Open" and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it's not a chronic diagnosis | Instead of just listing an HCC as closed, more detail is provided which presents an opportunity to improve future HCC recapture initiatives. diff --git a/models/data_marts/hcc_recapture/final_models.yml b/models/data_marts/hcc_recapture/final_models.yml index 6c634df59..ddf52c6e6 100644 --- a/models/data_marts/hcc_recapture/final_models.yml +++ b/models/data_marts/hcc_recapture/final_models.yml @@ -198,11 +198,31 @@ models: - 'new': defined as an hcc that has not been coded in the past 2 years - 'open': for gaps and claims, it's a chronic condition appropriate for recapture that has not been documented in current collection year - 'ineligible for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. + tests: + accepted_values: + values: ['closed using higher coefficient hcc in hierarchy group', 'closed', 'closed using lower coefficient hcc in hierarchy group', 'new', 'open', 'ineligible for recapture'] - name: recapturable_flag description: > Whether or not the HCC is recapturable. Includes the following values: - '1': condition is a chronic condition appropriate for recapture and has been documented in the prior 2 years - '0': condition is not a chronic condition appropriate for recapture or has not been documented in the prior 2 years + - name: hcc_type + description: > + The type of HCC. Includes the following values: + - 'coded': condition was coded in a claim during the collection year + - 'captured': condition is considered captured according to a different source besides claims, such as a clinical source + - 'suspect': condition is suspected to be present based on some other criteria besides claims diagnoses + tests: + accepted_values: + values: ['coded', 'captured', 'suspect'] + - name: hcc_source + description: > + The source of the HCC information. Includes the following values: + - 'payer': HCC was identified in data received from the payer such as claims or suspect lists + - 'clinical': HCC was identified in clinical data such as the EHR + tests: + accepted_values: + values: ['payer', 'clinical'] - name: hcc_recapture__gap_status description: The gap status for each HCC. From 331bf379593e5839bdb954d8e3173e6f39c3e447 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 09:44:31 -0700 Subject: [PATCH 35/40] fixing missing - --- models/data_marts/hcc_recapture/final_models.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/data_marts/hcc_recapture/final_models.yml b/models/data_marts/hcc_recapture/final_models.yml index ddf52c6e6..46b78cdce 100644 --- a/models/data_marts/hcc_recapture/final_models.yml +++ b/models/data_marts/hcc_recapture/final_models.yml @@ -199,7 +199,7 @@ models: - 'open': for gaps and claims, it's a chronic condition appropriate for recapture that has not been documented in current collection year - 'ineligible for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. tests: - accepted_values: + - accepted_values: values: ['closed using higher coefficient hcc in hierarchy group', 'closed', 'closed using lower coefficient hcc in hierarchy group', 'new', 'open', 'ineligible for recapture'] - name: recapturable_flag description: > @@ -213,7 +213,7 @@ models: - 'captured': condition is considered captured according to a different source besides claims, such as a clinical source - 'suspect': condition is suspected to be present based on some other criteria besides claims diagnoses tests: - accepted_values: + - accepted_values: values: ['coded', 'captured', 'suspect'] - name: hcc_source description: > @@ -221,7 +221,7 @@ models: - 'payer': HCC was identified in data received from the payer such as claims or suspect lists - 'clinical': HCC was identified in clinical data such as the EHR tests: - accepted_values: + - accepted_values: values: ['payer', 'clinical'] - name: hcc_recapture__gap_status From 2ae6eb71dc901e07719a12f96b93fdceeacf857d Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 09:52:22 -0700 Subject: [PATCH 36/40] fixing indentation --- docs/docs/data-marts/hcc-recapture.md | 10 ++++++++ .../data_marts/hcc_recapture/final_models.yml | 6 ++--- .../hcc_recapture__stg_chronic_hccs.sql | 6 ++--- .../hcc_recapture__stg_suspect_hccs.sql | 24 +++++++++---------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/docs/docs/data-marts/hcc-recapture.md b/docs/docs/data-marts/hcc-recapture.md index 8a5d8f8e7..278b07eac 100644 --- a/docs/docs/data-marts/hcc-recapture.md +++ b/docs/docs/data-marts/hcc-recapture.md @@ -39,3 +39,13 @@ When calculating HCC gap closure, YTD recapture curves are often used. Recapture All of the models below are the final models output from the HCC recapture data mart. +## Customizations +The following options are customizable in the HCC recapture mart to provide greater flexibility. + +### HCC Suspect Lists + +The `hcc_recapture_suspect_list` variable can be set to `true` in the `dbt_project.yml` in order to provide your own HCC suspect list from a payer or clinical source. The data needs to be input into a model called `suspect_hccs`. The required fields can be found in the `hcc_recapture__stg_suspect_hccs` model. + +### Chronic HCCs + +The `hcc_recapture_chronic_hccs` variable can be set to `true` in the `dbt_project.yml` in order ot provide your own custom chronic HCC definition instead of using the CMS chronic HCC definitions already provided. The data needs to be input into a model called `chronic_hccs`. The required fields can be found in the `hcc_recapture__stg_chronic_hccs` model. \ No newline at end of file diff --git a/models/data_marts/hcc_recapture/final_models.yml b/models/data_marts/hcc_recapture/final_models.yml index 46b78cdce..1a4c0d029 100644 --- a/models/data_marts/hcc_recapture/final_models.yml +++ b/models/data_marts/hcc_recapture/final_models.yml @@ -200,7 +200,7 @@ models: - 'ineligible for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. tests: - accepted_values: - values: ['closed using higher coefficient hcc in hierarchy group', 'closed', 'closed using lower coefficient hcc in hierarchy group', 'new', 'open', 'ineligible for recapture'] + values: ['closed using higher coefficient hcc in hierarchy group', 'closed', 'closed using lower coefficient hcc in hierarchy group', 'new', 'open', 'ineligible for recapture'] - name: recapturable_flag description: > Whether or not the HCC is recapturable. Includes the following values: @@ -214,7 +214,7 @@ models: - 'suspect': condition is suspected to be present based on some other criteria besides claims diagnoses tests: - accepted_values: - values: ['coded', 'captured', 'suspect'] + values: ['coded', 'captured', 'suspect'] - name: hcc_source description: > The source of the HCC information. Includes the following values: @@ -222,7 +222,7 @@ models: - 'clinical': HCC was identified in clinical data such as the EHR tests: - accepted_values: - values: ['payer', 'clinical'] + values: ['payer', 'clinical'] - name: hcc_recapture__gap_status description: The gap status for each HCC. diff --git a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_chronic_hccs.sql b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_chronic_hccs.sql index 0ff820f68..1bd6b3486 100644 --- a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_chronic_hccs.sql +++ b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_chronic_hccs.sql @@ -2,9 +2,9 @@ {% if var('hcc_recapture_chronic_hccs') %} select - hcc_code - , model_version - , chronic_flag + cast(hcc_code as {{dbt.type_string()}}) as hcc_code + , cast(model_version as {{dbt.type_string()}}) as model_version + , cast(chronic_flag as {{dbt.type_int()}}) as chronic_flag from {{ ref('chronic_hccs') }} {% else %} diff --git a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_suspect_hccs.sql b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_suspect_hccs.sql index 73e7df372..0e9756a91 100644 --- a/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_suspect_hccs.sql +++ b/models/data_marts/hcc_recapture/staging/hcc_recapture__stg_suspect_hccs.sql @@ -4,16 +4,16 @@ }} select - person_id - , payer - , data_source - , recorded_date - , model_version - , claim_id - , hcc_code - , hcc_description - , suspect_hcc_flag - , eligible_claim_flag - , hcc_type - , hcc_source + cast(person_id as {{dbt.type_string()}}) as person_id + , cast(payer as {{dbt.type_string()}}) as payer + , cast(data_source as {{dbt.type_string()}}) as data_source + , cast(recorded_date as date) as recorded_date + , cast(model_version as {{dbt.type_string()}}) as model_version + , cast(claim_id as {{dbt.type_string()}}) as claim_id + , cast(hcc_code as {{dbt.type_string()}}) as hcc_code + , cast(hcc_description as {{dbt.type_string()}}) as hcc_description + , cast(suspect_hcc_flag as {{dbt.type_int()}}) as suspect_hcc_flag + , cast(eligible_claim_flag as {{dbt.type_int()}}) as eligible_claim_flag + , cast(hcc_type as {{dbt.type_string()}}) as hcc_type + , cast(hcc_source as {{dbt.type_string()}}) as hcc_source from {{ ref('suspect_hccs')}} From bd3264b1bd3c2fcb670f023f0db9c8b8dcad3e23 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 09:57:07 -0700 Subject: [PATCH 37/40] fixing accepted values --- models/data_marts/hcc_recapture/final_models.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/data_marts/hcc_recapture/final_models.yml b/models/data_marts/hcc_recapture/final_models.yml index 1a4c0d029..c85357641 100644 --- a/models/data_marts/hcc_recapture/final_models.yml +++ b/models/data_marts/hcc_recapture/final_models.yml @@ -192,15 +192,15 @@ models: - name: gap_status description: > definitions for gap_status: - - 'closed using higher coefficient hcc in hierarchy group': An HCC in the same group was closed, but its coefficient is greater than the prior year HCC + - 'closed - higher coefficient hcc in hierarchy group': An HCC in the same group was closed, but its coefficient is greater than the prior year HCC - 'closed': the specific HCC in question has been observed in a risk adjustable claim during the collection year. - - 'closed using lower coefficient hcc in hierarchy group': An HCC in the same group was closed, but its coefficient is less than the prior year HCC + - 'closed - lower coefficient hcc in hierarchy group': An HCC in the same group was closed, but its coefficient is less than the prior year HCC - 'new': defined as an hcc that has not been coded in the past 2 years - 'open': for gaps and claims, it's a chronic condition appropriate for recapture that has not been documented in current collection year - 'ineligible for recapture': the specific HCC in question is “Open” and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it’s not a chronic diagnosis. tests: - accepted_values: - values: ['closed using higher coefficient hcc in hierarchy group', 'closed', 'closed using lower coefficient hcc in hierarchy group', 'new', 'open', 'ineligible for recapture'] + values: ['closed - higher coefficient hcc in hierarchy group', 'closed', 'closed - lower coefficient hcc in hierarchy group', 'new', 'open', 'ineligible for recapture'] - name: recapturable_flag description: > Whether or not the HCC is recapturable. Includes the following values: From 5379f3dc5d9bae4ef28fb7c98434b9da87234d13 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 10:04:55 -0700 Subject: [PATCH 38/40] updating docs --- docs/docs/data-marts/hcc-recapture.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/data-marts/hcc-recapture.md b/docs/docs/data-marts/hcc-recapture.md index 278b07eac..e4930fbac 100644 --- a/docs/docs/data-marts/hcc-recapture.md +++ b/docs/docs/data-marts/hcc-recapture.md @@ -25,9 +25,9 @@ The type of gap closure if provided using the `gap_status` field. Here are the g | Gap Status | Definition | |------------|------------| -| closed using higher coefficient hcc in hierarchy group | An HCC in the same group was closed, but its coefficient is greater than the prior year HCC | +| closed - higher coefficient hcc in hierarchy group | An HCC in the same group was closed, but its coefficient is greater than the prior year HCC | | closed | The specific HCC in question has been observed in a risk adjustable claim during the collection year | -| closed using lower coefficient hcc in hierarchy group | An HCC in the same group was closed, but its coefficient is less than the prior year HCC | +| closed - lower coefficient hcc in hierarchy group | An HCC in the same group was closed, but its coefficient is less than the prior year HCC | | new | Defined as an HCC that has not been coded in the past 2 years | | open | For gaps and claims, it's a chronic condition appropriate for recapture that has not been documented in current collection year | | ineligible for recapture | The specific HCC in question is "Open" and no related/equivalent HCC has been closed, but it is not appropriate for risk adjustment because it's not a chronic diagnosis | @@ -48,4 +48,4 @@ The `hcc_recapture_suspect_list` variable can be set to `true` in the `dbt_proje ### Chronic HCCs -The `hcc_recapture_chronic_hccs` variable can be set to `true` in the `dbt_project.yml` in order ot provide your own custom chronic HCC definition instead of using the CMS chronic HCC definitions already provided. The data needs to be input into a model called `chronic_hccs`. The required fields can be found in the `hcc_recapture__stg_chronic_hccs` model. \ No newline at end of file +The `hcc_recapture_chronic_hccs` variable can be set to `true` in the `dbt_project.yml` in order to provide your own custom chronic HCC definition instead of using the CMS chronic HCC definitions already provided. The data needs to be input into a model called `chronic_hccs`. The required fields can be found in the `hcc_recapture__stg_chronic_hccs` model. \ No newline at end of file From dc24df1721aa05d32ca883129a2826d8668d3df7 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 11:24:25 -0700 Subject: [PATCH 39/40] adding hierarchy filter --- .../final/hcc_recapture__recapture_rates_monthly.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql b/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql index 7f1e76716..54c172b38 100644 --- a/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql +++ b/models/data_marts/hcc_recapture/final/hcc_recapture__recapture_rates_monthly.sql @@ -27,6 +27,7 @@ where 1=1 and gap_status not in ('ineligible for recapture', 'new') and hcc_type in ('captured', 'coded') and recapturable_flag = 1 + and filtered_by_hierarchy_flag = 0 ) , monthly_hcc_counts as ( From 8ccc6ee0be048e912c064e67d47c683a68970ff1 Mon Sep 17 00:00:00 2001 From: Andreas Martinson Date: Wed, 29 Apr 2026 16:03:03 -0700 Subject: [PATCH 40/40] adding back erroneous merge conflict --- ...lized_input__int_medical_npi_normalize.sql | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql index 0470e4da3..fe13855de 100644 --- a/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql +++ b/models/normalization/intermediate/normalized_input__int_medical_npi_normalize.sql @@ -41,3 +41,33 @@ left outer join {{ ref('provider_data__provider') }} as fac_prov on med.facility_npi = fac_prov.npi and fac_prov.entity_type_description = 'Organization' and med.claim_type = 'institutional' +) + +, facility_npi_ranked as ( +select + data_source + , claim_id + , normalized_facility_npi + , normalized_facility_name + , row_number() over (partition by data_source, claim_id order by claim_line_number) as facility_npi_rank +from base +where normalized_facility_npi is not null +) + +select + base.claim_id + , base.claim_line_number + , base.claim_type + , base.data_source + , base.normalized_rendering_npi + , base.normalized_rendering_name + , base.normalized_billing_npi + , base.normalized_billing_name + , fac.normalized_facility_npi + , fac.normalized_facility_name + , base.tuva_last_run +from base +left join facility_npi_ranked as fac + on base.claim_id = fac.claim_id + and base.data_source = fac.data_source + and facility_npi_rank = 1 \ No newline at end of file