diff --git a/README.md b/README.md index ee7bab9..6e9f395 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A Cumulus-based implementation of the [qualifier metrics](https://github.com/syn ## Implemented Metrics -The following qualifier metrics are implemented (per January 2025 qualifer definitions). +The following qualifier metrics are implemented (per February 2025 qualifer definitions). - [c_attachment_count](https://github.com/sync-for-science/qualifier/blob/master/metrics.md#c_attachment_count) - [c_content_type_use](https://github.com/sync-for-science/qualifier/blob/master/metrics.md#c_content_type_use) diff --git a/cumulus_library_data_metrics/__init__.py b/cumulus_library_data_metrics/__init__.py index 7f630d2..969ec89 100644 --- a/cumulus_library_data_metrics/__init__.py +++ b/cumulus_library_data_metrics/__init__.py @@ -1,3 +1,3 @@ """Data Metrics study for Cumulus Library""" -__version__ = "7.0.2" +__version__ = "8.0.0" diff --git a/cumulus_library_data_metrics/base.py b/cumulus_library_data_metrics/base.py index d1577f1..298a2ac 100644 --- a/cumulus_library_data_metrics/base.py +++ b/cumulus_library_data_metrics/base.py @@ -152,7 +152,7 @@ def render_sql(self, template: str, **kwargs) -> str: kwargs["dates"] = self.date_fields.get(src) kwargs["patient_field"] = resource_info.PATIENTS.get(src) kwargs["schema"] = self.schemas.get(src) - kwargs.update(resource_info.CATEGORIES.get(src, {})) + kwargs["cat_info"] = resource_info.CATEGORIES.get(src, {}) # See how we should combine counts. kwargs["output_mode"] = self.output_mode diff --git a/cumulus_library_data_metrics/c_resource_count/c_resource_count.jinja b/cumulus_library_data_metrics/c_resource_count/c_resource_count.jinja index 80ed914..fded527 100644 --- a/cumulus_library_data_metrics/c_resource_count/c_resource_count.jinja +++ b/cumulus_library_data_metrics/c_resource_count/c_resource_count.jinja @@ -1,17 +1,16 @@ {% import 'utils.jinja' as utils %} CREATE TABLE {{ study_prefix }}__count_c_resource_count_{{ src|lower }}_{{ period|lower }} AS ( -{%- set orig_cat_field = cat_field %} WITH src_status AS {{ utils.extract_status(src) }}, -src_category AS {{ utils.extract_category_codes(src, cat_field, cat_systems) }}, +src_category AS {{ utils.extract_category_codes(src, cat_info) }}, simplified AS ( SELECT src.id, - {%- if cat_field %} - {{ utils.coalesce_missing('src_category.category') }} AS {{ cat_field }}, + {%- if cat_info %} + {{ utils.coalesce_missing('src_category.category') }} AS {{ cat_info["cat_field"] }}, {%- endif %} -- Parse string datetime into a single period string @@ -30,8 +29,8 @@ simplified AS ( ) {% call utils.make_counts('simplified', output_mode) %} - {%- if cat_field %} - {{ cat_field }}, + {%- if cat_info %} + {{ cat_info["cat_field"] }}, {%- endif %} {%- if dates %} diff --git a/cumulus_library_data_metrics/c_resources_per_pt/c_resources_per_pt.jinja b/cumulus_library_data_metrics/c_resources_per_pt/c_resources_per_pt.jinja index 148bc30..2091f75 100644 --- a/cumulus_library_data_metrics/c_resources_per_pt/c_resources_per_pt.jinja +++ b/cumulus_library_data_metrics/c_resources_per_pt/c_resources_per_pt.jinja @@ -12,11 +12,7 @@ patient_refs AS ( {% for resource, field_name in patient_fields.items() %} cat_slice_{{ resource|lower }}_codes AS -{{ utils.extract_category_codes( - resource, - categories.get(resource, {}).get("cat_field"), - categories.get(resource, {}).get("cat_systems"), -) }}, +{{ utils.extract_category_codes(resource, categories.get(resource, {})) }}, {% endfor %} counts_by_pt AS ( diff --git a/cumulus_library_data_metrics/c_system_use/c_system_use.jinja b/cumulus_library_data_metrics/c_system_use/c_system_use.jinja index 83d4009..5e59994 100644 --- a/cumulus_library_data_metrics/c_system_use/c_system_use.jinja +++ b/cumulus_library_data_metrics/c_system_use/c_system_use.jinja @@ -6,7 +6,7 @@ WITH src_status AS {{ utils.extract_status(src) }}, {% if use_category %} -src_categories AS {{ utils.extract_codes(src, cat_field, system=cat_systems, is_array=true) }}, +src_categories AS {{ utils.extract_category_codes(src, cat_info) }}, {% endif %} src_systems_flat AS ( @@ -58,7 +58,7 @@ simplified AS ( {% endif %} {% if use_category %} - {{ utils.array_to_string('src_categories.codes') }} AS category, + {{ utils.coalesce_missing('src_categories.category') }} AS category, {% endif %} {{ utils.array_to_string('src_systems.systems') }} AS systems, diff --git a/cumulus_library_data_metrics/meta/version.jinja b/cumulus_library_data_metrics/meta/version.jinja index 2354f3e..7e489e0 100644 --- a/cumulus_library_data_metrics/meta/version.jinja +++ b/cumulus_library_data_metrics/meta/version.jinja @@ -1,2 +1,2 @@ CREATE TABLE {{ study_prefix }}__meta_version AS -SELECT 2 AS data_package_version; +SELECT 3 AS data_package_version; diff --git a/cumulus_library_data_metrics/resource_info.py b/cumulus_library_data_metrics/resource_info.py index 9302bba..9104e15 100644 --- a/cumulus_library_data_metrics/resource_info.py +++ b/cumulus_library_data_metrics/resource_info.py @@ -31,9 +31,10 @@ "cat_systems": [systems.USCORE_DOCREF_CATEGORY], }, "Encounter": { - "cat_field": "type", - # https://hl7.org/fhir/us/core/STU4/ValueSet-us-core-encounter-type.html - "cat_systems": [systems.CPT, systems.SNOMED], + "cat_field": "class", + # https://www.hl7.org/fhir/R4/v3/ActEncounterCode/vs.html + "cat_systems": [systems.ENCOUNTER_CLASS], + "is_coding": True, }, "MedicationRequest": { "cat_field": "category", @@ -50,7 +51,7 @@ # Date fields in preference order. # This prefers "interaction with health system" dates, then administrative dates like "issued", # then best effort start dates like onsetDateTime. -# See https://github.com/smart-on-fhir/cumulus-library-data-metrics/issues/16 for more. +# See https://github.com/sync-for-science/qualifier/blob/master/metrics.md#by-date DATES = { "AllergyIntolerance": ["recordedDate", "onsetDateTime", "onsetPeriod.start", "onsetPeriod.end"], "Condition": [ diff --git a/cumulus_library_data_metrics/systems.py b/cumulus_library_data_metrics/systems.py index f53779f..eae32f6 100644 --- a/cumulus_library_data_metrics/systems.py +++ b/cumulus_library_data_metrics/systems.py @@ -14,6 +14,7 @@ # Resource-specific systems CONDITION_CATEGORY = "http://terminology.hl7.org/CodeSystem/condition-category" DIAGNOSTIC_SECTION = "http://terminology.hl7.org/CodeSystem/v2-0074" +ENCOUNTER_CLASS = "http://terminology.hl7.org/CodeSystem/v3-ActCode" MEDREQ_CATEGORY = "http://terminology.hl7.org/CodeSystem/medicationrequest-category" OBSERVATION_CATEGORY = "http://terminology.hl7.org/CodeSystem/observation-category" USCORE_CONDITION_CATEGORY = "http://hl7.org/fhir/us/core/CodeSystem/condition-category" diff --git a/cumulus_library_data_metrics/utils.jinja b/cumulus_library_data_metrics/utils.jinja index fac2bde..52beafd 100644 --- a/cumulus_library_data_metrics/utils.jinja +++ b/cumulus_library_data_metrics/utils.jinja @@ -1,18 +1,18 @@ -{% macro _compare_systems(system) -%} +{% macro _compare_systems(coding_field, system) -%} {#- Support both a list of systems or a single system #} {%- if system is string %} - c.coding.system = '{{ system }}' + {{ coding_field }}.system = '{{ system }}' {%- elif system %} ( FALSE -- just here so rest of these can start with OR {%- for sys_info in system %} {#- sys_info can either by a string or a list of string then codes #} {%- if sys_info is string %} - OR c.coding.system = '{{ sys_info }}' + OR {{ coding_field }}.system = '{{ sys_info }}' {%- else %} OR ( - c.coding.system = '{{ sys_info[0] }}' - AND c.coding.code in ('{{ sys_info[1]|join("', '") }}') + {{ coding_field }}.system = '{{ sys_info[0] }}' + AND {{ coding_field }}.code in ('{{ sys_info[1]|join("', '") }}') ) {%- endif %} {%- endfor %} @@ -48,7 +48,7 @@ {%- endif %} WHERE c.coding.code IS NOT NULL - AND {{ _compare_systems(system) }} + AND {{ _compare_systems("c.coding", system) }} GROUP BY id ) @@ -72,7 +72,7 @@ {%- endif %} WHERE c.coding.code IS NOT NULL - AND {{ _compare_systems(system) }} + AND {{ _compare_systems("c.coding", system) }} ) {%- endmacro %} @@ -117,9 +117,17 @@ -- Extracts the category codes and returns a select with (id (string), category (string)). -{% macro extract_category_codes(src, cat_field, cat_systems) -%} +{% macro extract_category_codes(src, cat_info) -%} ( -{% if cat_systems %} +{%- set cat_systems = cat_info.get("cat_systems") %} +{%- set cat_field = cat_info.get("cat_field") %} +{%- set is_coding = cat_info.get("is_coding", False) %} +{% if is_coding %} + {# TODO: handle array & no-systems cases. But this works for now. #} + SELECT id, {{ cat_field }}.code AS category + FROM {{ src }} + WHERE {{ _compare_systems(cat_field, cat_systems) }} +{% elif cat_systems %} WITH array_codes AS {{ extract_codes(src, cat_field, system=cat_systems, is_array=true) }} SELECT id, {{ array_to_string('codes') }} AS category diff --git a/tests/data/c_resource_count/general/encounter/0.ndjson b/tests/data/c_resource_count/general/encounter/0.ndjson index 778ab90..78bca80 100644 --- a/tests/data/c_resource_count/general/encounter/0.ndjson +++ b/tests/data/c_resource_count/general/encounter/0.ndjson @@ -1,6 +1,5 @@ -{"resourceType": "Encounter", "id": "with-date", "type": [{"coding": [{"code": "abcd", "system": "http://www.ama-assn.org/go/cpt"}]}], "period": {"start": "2023-10-04"}} -{"resourceType": "Encounter", "id": "no-date", "type": [{"coding": [{"code": "1234", "system": "http://snomed.info/sct"}]}]} -{"resourceType": "Encounter", "id": "multiple-codings", "type": [{"coding": [{"code": "abcd", "system": "http://www.ama-assn.org/go/cpt"}, {"code": "1234", "system": "http://snomed.info/sct"}]}], "period": {"start": "2023-10-04"}} -{"resourceType": "Encounter", "id": "unrelated-category", "type": [{"coding": [{"code": "nope", "system": "nope"}]}]} +{"resourceType": "Encounter", "id": "with-date", "class": {"code": "AMB", "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode"}, "period": {"start": "2023-10-04"}} +{"resourceType": "Encounter", "id": "no-date", "class": {"code": "EMER", "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode"}} +{"resourceType": "Encounter", "id": "unrelated-category", "class": {"code": "nope", "system": "nope"}} {"resourceType": "Encounter", "id": "no-category", "period": {"start": "2022-06-04"}} {"resourceType": "Encounter", "id": "nothing"} diff --git a/tests/data/c_resource_count/general/expected_encounter_month.csv b/tests/data/c_resource_count/general/expected_encounter_month.csv index aca0a02..0d1da3a 100644 --- a/tests/data/c_resource_count/general/expected_encounter_month.csv +++ b/tests/data/c_resource_count/general/expected_encounter_month.csv @@ -1,6 +1,5 @@ -cnt,type,month,status +cnt,class,month,status 2,cumulus__none,cumulus__none,cumulus__none 1,cumulus__none,2022-06,cumulus__none -1,abcd,2023-10,cumulus__none -1,1234; abcd,2023-10,cumulus__none -1,1234,cumulus__none,cumulus__none +1,EMER,cumulus__none,cumulus__none +1,AMB,2023-10,cumulus__none diff --git a/tests/data/c_resource_count/general/expected_encounter_year.csv b/tests/data/c_resource_count/general/expected_encounter_year.csv index 3f9c1f1..4bf9628 100644 --- a/tests/data/c_resource_count/general/expected_encounter_year.csv +++ b/tests/data/c_resource_count/general/expected_encounter_year.csv @@ -1,6 +1,5 @@ -cnt,type,year,status +cnt,class,year,status 2,cumulus__none,cumulus__none,cumulus__none 1,cumulus__none,2022,cumulus__none -1,abcd,2023,cumulus__none -1,1234; abcd,2023,cumulus__none -1,1234,cumulus__none,cumulus__none +1,EMER,cumulus__none,cumulus__none +1,AMB,2023,cumulus__none diff --git a/tests/data/meta/general/expected_version.csv b/tests/data/meta/general/expected_version.csv index 49bfa56..4d65fc4 100644 --- a/tests/data/meta/general/expected_version.csv +++ b/tests/data/meta/general/expected_version.csv @@ -1,2 +1,2 @@ data_package_version -2 +3