diff --git a/CHANGELOG.md b/CHANGELOG.md index 3196a58..7ae15b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# Samply.Focus v0.24.0 2026-05-29 + +## Major changes + +* BBMRI: stop checking diagnosis in Specimen + # Samply.Focus v0.23.0 2026-04-28 ## Major changes diff --git a/Cargo.toml b/Cargo.toml index 2452481..636c666 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "focus" -version = "0.23.0" +version = "0.24.0" edition = "2021" license = "Apache-2.0" @@ -31,6 +31,7 @@ rand = { default-features = false, version = "0.9.0" } futures-util = { version = "0.3", default-features = false, features = ["std"] } tryhard = "0.5" + # Logging tracing = { version = "0.1.37", default-features = false } tracing-subscriber = { version = "0.3.11", default-features = false, features = [ @@ -58,17 +59,13 @@ sqlx = { version = "0.8.2", features = [ "chrono", "rust_decimal", "uuid", -], optional = true } +]} kurtbuilds_sqlx_serde = { version = "0.3.2", features = [ "json", "decimal", "chrono", "uuid", -], optional = true } - -[features] -default = ["query-sql"] -query-sql = ["dep:sqlx", "dep:kurtbuilds_sqlx_serde"] +]} [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/README.md b/README.md index d288233..54d86b7 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ AUTH_HEADER = "[Auth Type] XXXX" #Authorization header for accessing the store; EXPORTER_API_KEY = "XXXX" # Value of header x-api-key for accessing the Exporter application ``` -In order to use Postgres querying, a Docker image built with the feature "dktk" needs to be used and this optional variable set: +In order to use Postgres querying this optional variable needs to be set: ```bash POSTGRES_CONNECTION_STRING = "postgresql://postgres:Test.123@localhost:5432/postgres" # Postgres connection string ``` diff --git a/resources/test/query_bbmri.cql b/resources/test/query_bbmri.cql deleted file mode 100644 index 90c0986..0000000 --- a/resources/test/query_bbmri.cql +++ /dev/null @@ -1,91 +0,0 @@ -library Retrieve -using FHIR version '4.0.0' -include FHIRHelpers version '4.0.0' - -codesystem loinc: 'http://loinc.org' - -codesystem icd10: 'http://hl7.org/fhir/sid/icd-10' - -codesystem SampleMaterialType: 'https://fhir.bbmri.de/CodeSystem/SampleMaterialType' - -context Patient - -define AgeClass: - (AgeInYears() div 10) * 10 - - -define Gender: - if (Patient.gender is null) then 'unknown' - else if (Patient.gender != 'male' and Patient.gender != 'female' and Patient.gender != 'other' and Patient.gender != 'unknown') then 'other' - else Patient.gender - -define Custodian: - First(from Specimen.extension E - where E.url = 'https://fhir.bbmri.de/StructureDefinition/Custodian' - return (E.value as Reference).identifier.value) - - -define function SampleType(specimen FHIR.Specimen): - case FHIRHelpers.ToCode(specimen.type.coding.where(system = 'https://fhir.bbmri.de/CodeSystem/SampleMaterialType').first()) - when Code 'plasma-edta' from SampleMaterialType then 'blood-plasma' - when Code 'plasma-citrat' from SampleMaterialType then 'blood-plasma' - when Code 'plasma-heparin' from SampleMaterialType then 'blood-plasma' - when Code 'plasma-cell-free' from SampleMaterialType then 'blood-plasma' - when Code 'plasma-other' from SampleMaterialType then 'blood-plasma' - when Code 'plasma' from SampleMaterialType then 'blood-plasma' - when Code 'tissue-formalin' from SampleMaterialType then 'tissue-ffpe' - when Code 'tumor-tissue-ffpe' from SampleMaterialType then 'tissue-ffpe' - when Code 'normal-tissue-ffpe' from SampleMaterialType then 'tissue-ffpe' - when Code 'other-tissue-ffpe' from SampleMaterialType then 'tissue-ffpe' - when Code 'tumor-tissue-frozen' from SampleMaterialType then 'tissue-frozen' - when Code 'normal-tissue-frozen' from SampleMaterialType then 'tissue-frozen' - when Code 'other-tissue-frozen' from SampleMaterialType then 'tissue-frozen' - when Code 'tissue-paxgene-or-else' from SampleMaterialType then 'tissue-other' - when Code 'derivative' from SampleMaterialType then 'derivative-other' - when Code 'liquid' from SampleMaterialType then 'liquid-other' - when Code 'tissue' from SampleMaterialType then 'tissue-other' - when Code 'serum' from SampleMaterialType then 'blood-serum' - when Code 'cf-dna' from SampleMaterialType then 'dna' - when Code 'g-dna' from SampleMaterialType then 'dna' - when Code 'blood-plasma' from SampleMaterialType then 'blood-plasma' - when Code 'tissue-ffpe' from SampleMaterialType then 'tissue-ffpe' - when Code 'tissue-frozen' from SampleMaterialType then 'tissue-frozen' - when Code 'tissue-other' from SampleMaterialType then 'tissue-other' - when Code 'derivative-other' from SampleMaterialType then 'derivative-other' - when Code 'liquid-other' from SampleMaterialType then 'liquid-other' - when Code 'blood-serum' from SampleMaterialType then 'blood-serum' - when Code 'dna' from SampleMaterialType then 'dna' - when Code 'buffy-coat' from SampleMaterialType then 'buffy-coat' - when Code 'urine' from SampleMaterialType then 'urine' - when Code 'ascites' from SampleMaterialType then 'ascites' - when Code 'saliva' from SampleMaterialType then 'saliva' - when Code 'csf-liquor' from SampleMaterialType then 'csf-liquor' - when Code 'bone-marrow' from SampleMaterialType then 'bone-marrow' - when Code 'peripheral-blood-cells-vital' from SampleMaterialType then 'peripheral-blood-cells-vital' - when Code 'stool-faeces' from SampleMaterialType then 'stool-faeces' - when Code 'rna' from SampleMaterialType then 'rna' - when Code 'whole-blood' from SampleMaterialType then 'whole-blood' - when Code 'swab' from SampleMaterialType then 'swab' - when Code 'dried-whole-blood' from SampleMaterialType then 'dried-whole-blood' - when null then 'Unknown' - else 'Unknown' - end - - -define Specimen: - - if InInitialPopulation then [Specimen] else {} as List - -define Diagnosis: - if InInitialPopulation then [Condition] else {} as List - define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): - Coalesce(condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first()) - - - -define InInitialPopulation: - -true \ No newline at end of file diff --git a/resources/test/query_bbmri_placeholders.cql b/resources/test/query_bbmri_placeholders.cql deleted file mode 100644 index f356da6..0000000 --- a/resources/test/query_bbmri_placeholders.cql +++ /dev/null @@ -1,27 +0,0 @@ -library Retrieve -using FHIR version '4.0.0' -include FHIRHelpers version '4.0.0' - -codesystem loinc: 'http://loinc.org' - -codesystem icd10: 'http://hl7.org/fhir/sid/icd-10' - -codesystem SampleMaterialType: 'https://fhir.bbmri.de/CodeSystem/SampleMaterialType' - -context Patient - -BBMRI_STRAT_AGE_STRATIFIER - -BBMRI_STRAT_GENDER_STRATIFIER - -BBMRI_STRAT_CUSTODIAN_STRATIFIER - -BBMRI_STRAT_SAMPLE_TYPE_STRATIFIER - -BBMRI_STRAT_DEF_SPECIMEN - if InInitialPopulation then [Specimen] else {} as List - -BBMRI_STRAT_DIAGNOSIS_STRATIFIER - -BBMRI_STRAT_DEF_IN_INITIAL_POPULATION -true \ No newline at end of file diff --git a/resources/test/result_after.cql b/resources/test/result_after.cql index be3807e..9dd7ff5 100644 --- a/resources/test/result_after.cql +++ b/resources/test/result_after.cql @@ -71,15 +71,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: ((((exists from [Specimen] S diff --git a/resources/test/result_age_at_diagnosis_30_to_70.cql b/resources/test/result_age_at_diagnosis_30_to_70.cql index 8d4ce81..845d2cd 100644 --- a/resources/test/result_age_at_diagnosis_30_to_70.cql +++ b/resources/test/result_age_at_diagnosis_30_to_70.cql @@ -71,15 +71,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: ((((exists from [Condition] C diff --git a/resources/test/result_age_at_diagnosis_lower_than_70.cql b/resources/test/result_age_at_diagnosis_lower_than_70.cql index 9230612..417caef 100644 --- a/resources/test/result_age_at_diagnosis_lower_than_70.cql +++ b/resources/test/result_age_at_diagnosis_lower_than_70.cql @@ -71,15 +71,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: ((((exists from [Condition] C diff --git a/resources/test/result_all_gbn.cql b/resources/test/result_all_gbn.cql index d167d93..aa135a6 100644 --- a/resources/test/result_all_gbn.cql +++ b/resources/test/result_all_gbn.cql @@ -76,18 +76,14 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: -((((Patient.gender = 'male') or (Patient.gender = 'other'))) and ((((((exists[Condition: Code 'C25' from icd10]) or (exists[Condition: Code 'C25' from icd10gm]) or (exists[Condition: Code 'C25' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C25'))))) or (((((exists[Condition: Code 'C56' from icd10]) or (exists[Condition: Code 'C56' from icd10gm]) or (exists[Condition: Code 'C56' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C56')))))) and (exists from [Condition] C +((((Patient.gender = 'male') or (Patient.gender = 'other'))) and ((((((exists[Condition: Code 'C25' from icd10]) or (exists[Condition: Code 'C25' from icd10gm]) or (exists[Condition: Code 'C25' from icd10gmnew]))))) or (((((exists[Condition: Code 'C56' from icd10]) or (exists[Condition: Code 'C56' from icd10gm]) or (exists[Condition: Code 'C56' from icd10gmnew])))))) and (exists from [Condition] C where AgeInYearsAt(FHIRHelpers.ToDateTime(C.onset)) between Ceiling(10) and Ceiling(100)) and (exists from [Condition] C where FHIRHelpers.ToDateTime(C.onset) between @2023-09-30 and @2023-10-29) and (exists from [Observation: Code '39156-5' from loinc] O where ((O.value as Quantity) < 10 'kg/m2' and (O.value as Quantity) > 100 'kg/m2')) and (exists from [Observation: Code '29463-7' from loinc] O diff --git a/resources/test/result_before.cql b/resources/test/result_before.cql index 730a6d2..a18b652 100644 --- a/resources/test/result_before.cql +++ b/resources/test/result_before.cql @@ -71,15 +71,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: ((((exists from [Specimen] S diff --git a/resources/test/result_c61_and_male.cql b/resources/test/result_c61_and_male.cql index 3809a6f..b767a88 100644 --- a/resources/test/result_c61_and_male.cql +++ b/resources/test/result_c61_and_male.cql @@ -73,15 +73,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: -((((((((exists[Condition: Code 'C61' from icd10]) or (exists[Condition: Code 'C61' from icd10gm]) or (exists[Condition: Code 'C61' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C61')))))) and ((((Patient.gender = 'male')))))) \ No newline at end of file +((((((((exists[Condition: Code 'C61' from icd10]) or (exists[Condition: Code 'C61' from icd10gm]) or (exists[Condition: Code 'C61' from icd10gmnew])))))) and ((((Patient.gender = 'male')))))) \ No newline at end of file diff --git a/resources/test/result_current.cql b/resources/test/result_current.cql index ca99e6a..e912879 100644 --- a/resources/test/result_current.cql +++ b/resources/test/result_current.cql @@ -74,16 +74,12 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: -((((((Patient.gender = 'male')))) and ((((((exists[Condition: Code 'C61' from icd10]) or (exists[Condition: Code 'C61' from icd10gm]) or (exists[Condition: Code 'C61' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C61')))))) and (( AgeInYears() between Ceiling(10) and Ceiling(90)))) or (((exists from [Specimen] S +((((((Patient.gender = 'male')))) and ((((((exists[Condition: Code 'C61' from icd10]) or (exists[Condition: Code 'C61' from icd10gm]) or (exists[Condition: Code 'C61' from icd10gmnew])))))) and (( AgeInYears() between Ceiling(10) and Ceiling(90)))) or (((exists from [Specimen] S where FHIRHelpers.ToDateTime(S.collection.collected) between @1900-01-01 and @2024-10-25 )) and ((((exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/StorageTemperature').value.coding contains Code 'temperature2to10' from StorageTemperature) )))))) \ No newline at end of file diff --git a/resources/test/result_empty.cql b/resources/test/result_empty.cql index 54d8c01..1844321 100644 --- a/resources/test/result_empty.cql +++ b/resources/test/result_empty.cql @@ -71,15 +71,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: true \ No newline at end of file diff --git a/resources/test/result_greater.cql b/resources/test/result_greater.cql index d6c8fe1..63af3aa 100644 --- a/resources/test/result_greater.cql +++ b/resources/test/result_greater.cql @@ -71,15 +71,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: ((((exists from [Condition] C diff --git a/resources/test/result_lens2.cql b/resources/test/result_lens2.cql index 83ceede..39d3996 100644 --- a/resources/test/result_lens2.cql +++ b/resources/test/result_lens2.cql @@ -74,15 +74,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: -((((((Patient.gender = 'male'))) or (((Patient.gender = 'female')))) and ((((((exists[Condition: Code 'C41' from icd10]) or (exists[Condition: Code 'C41' from icd10gm]) or (exists[Condition: Code 'C41' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C41'))))) or (((((exists[Condition: Code 'C50' from icd10]) or (exists[Condition: Code 'C50' from icd10gm]) or (exists[Condition: Code 'C50' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C50')))))) and (((( exists [Specimen: Code 'tissue-frozen' from SampleMaterialType]) or ( exists [Specimen: Code 'tumor-tissue-frozen' from SampleMaterialType]) or ( exists [Specimen: Code 'normal-tissue-frozen' from SampleMaterialType]) or ( exists [Specimen: Code 'other-tissue-frozen' from SampleMaterialType]))) or ((( exists [Specimen: Code 'blood-serum' from SampleMaterialType]) or ( exists [Specimen: Code 'serum' from SampleMaterialType]))))) or (((((Patient.gender = 'male')))) and ((((((exists[Condition: Code 'C41' from icd10]) or (exists[Condition: Code 'C41' from icd10gm]) or (exists[Condition: Code 'C41' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C41'))))) or (((((exists[Condition: Code 'C50' from icd10]) or (exists[Condition: Code 'C50' from icd10gm]) or (exists[Condition: Code 'C50' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C50')))))) and (((( exists [Specimen: Code 'liquid-other' from SampleMaterialType]) or ( exists [Specimen: Code 'liquid' from SampleMaterialType]))) or ((( exists [Specimen: Code 'rna' from SampleMaterialType]))) or ((( exists [Specimen: Code 'urine' from SampleMaterialType])))) and ((((exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/StorageTemperature').value.coding contains Code 'temperatureRoom' from StorageTemperature) ))) or (((exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/StorageTemperature').value.coding contains Code 'four_degrees' from StorageTemperature) )))))) \ No newline at end of file +((((((Patient.gender = 'male'))) or (((Patient.gender = 'female')))) and ((((((exists[Condition: Code 'C41' from icd10]) or (exists[Condition: Code 'C41' from icd10gm]) or (exists[Condition: Code 'C41' from icd10gmnew]))))) or (((((exists[Condition: Code 'C50' from icd10]) or (exists[Condition: Code 'C50' from icd10gm]) or (exists[Condition: Code 'C50' from icd10gmnew])))))) and (((( exists [Specimen: Code 'tissue-frozen' from SampleMaterialType]) or ( exists [Specimen: Code 'tumor-tissue-frozen' from SampleMaterialType]) or ( exists [Specimen: Code 'normal-tissue-frozen' from SampleMaterialType]) or ( exists [Specimen: Code 'other-tissue-frozen' from SampleMaterialType]))) or ((( exists [Specimen: Code 'blood-serum' from SampleMaterialType]) or ( exists [Specimen: Code 'serum' from SampleMaterialType]))))) or (((((Patient.gender = 'male')))) and ((((((exists[Condition: Code 'C41' from icd10]) or (exists[Condition: Code 'C41' from icd10gm]) or (exists[Condition: Code 'C41' from icd10gmnew]))))) or (((((exists[Condition: Code 'C50' from icd10]) or (exists[Condition: Code 'C50' from icd10gm]) or (exists[Condition: Code 'C50' from icd10gmnew])))))) and (((( exists [Specimen: Code 'liquid-other' from SampleMaterialType]) or ( exists [Specimen: Code 'liquid' from SampleMaterialType]))) or ((( exists [Specimen: Code 'rna' from SampleMaterialType]))) or ((( exists [Specimen: Code 'urine' from SampleMaterialType])))) and ((((exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/StorageTemperature').value.coding contains Code 'temperatureRoom' from StorageTemperature) ))) or (((exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/StorageTemperature').value.coding contains Code 'four_degrees' from StorageTemperature) )))))) \ No newline at end of file diff --git a/resources/test/result_less.cql b/resources/test/result_less.cql index 5cb6a48..504b26b 100644 --- a/resources/test/result_less.cql +++ b/resources/test/result_less.cql @@ -71,15 +71,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: ((((exists from [Condition] C diff --git a/resources/test/result_male_or_female.cql b/resources/test/result_male_or_female.cql index b823b85..4f21968 100644 --- a/resources/test/result_male_or_female.cql +++ b/resources/test/result_male_or_female.cql @@ -71,15 +71,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: ((((((Patient.gender = 'male'))) or (((Patient.gender = 'female')))))) \ No newline at end of file diff --git a/resources/test/result_quote.cql b/resources/test/result_quote.cql index c9bfb98..8e408c4 100644 --- a/resources/test/result_quote.cql +++ b/resources/test/result_quote.cql @@ -73,15 +73,11 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: -((((((((exists[Condition: Code 'C61\"\'\\\n\r\t' from icd10]) or (exists[Condition: Code 'C61\"\'\\\n\r\t' from icd10gm]) or (exists[Condition: Code 'C61\"\'\\\n\r\t' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C61\"\'\\\n\r\t')))))) and ((((Patient.gender = 'male')))))) \ No newline at end of file +((((((((exists[Condition: Code 'C61\"\'\\\n\r\t' from icd10]) or (exists[Condition: Code 'C61\"\'\\\n\r\t' from icd10gm]) or (exists[Condition: Code 'C61\"\'\\\n\r\t' from icd10gmnew])))))) and ((((Patient.gender = 'male')))))) \ No newline at end of file diff --git a/resources/test/result_some_gbn.cql b/resources/test/result_some_gbn.cql index 8a76a3d..edd4d5b 100644 --- a/resources/test/result_some_gbn.cql +++ b/resources/test/result_some_gbn.cql @@ -76,18 +76,14 @@ define Diagnosis: if InInitialPopulation then [Condition] else {} as List define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: -((((Patient.gender = 'other') or (Patient.gender = 'male'))) and (((((exists[Condition: Code 'C24' from icd10]) or (exists[Condition: Code 'C24' from icd10gm]) or (exists[Condition: Code 'C24' from icd10gmnew])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains 'C24'))))) and (exists from [Condition] C +((((Patient.gender = 'other') or (Patient.gender = 'male'))) and (((((exists[Condition: Code 'C24' from icd10]) or (exists[Condition: Code 'C24' from icd10gm]) or (exists[Condition: Code 'C24' from icd10gmnew]))))) and (exists from [Condition] C where AgeInYearsAt(FHIRHelpers.ToDateTime(C.onset)) between Ceiling(1) and Ceiling(11)) and (exists from [Condition] C where FHIRHelpers.ToDateTime(C.onset) between @2023-10-29 and @2023-10-30) and (exists from [Observation: Code '39156-5' from loinc] O where ((O.value as Quantity) < 1 'kg/m2' and (O.value as Quantity) > 111 'kg/m2')) and (exists from [Observation: Code '29463-7' from loinc] O diff --git a/src/config.rs b/src/config.rs index 9d174a2..32144ff 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,11 +24,8 @@ pub enum EndpointType { Omop, // endpoint is URL of a query mediator translating AST to provider specific SQL EucaimApi, // endpoint is URL of custom API for querying EUCAIM provider EucaimBeacon, - #[cfg(feature = "query-sql")] EucaimSql, - #[cfg(feature = "query-sql")] BlazeAndSql, - #[cfg(feature = "query-sql")] Sql, } @@ -39,11 +36,8 @@ impl fmt::Display for EndpointType { EndpointType::Omop => write!(f, "omop"), EndpointType::EucaimApi => write!(f, "eucaim_api"), EndpointType::EucaimBeacon => write!(f, "eucaim_beacon"), - #[cfg(feature = "query-sql")] EndpointType::EucaimSql => write!(f, "eucaim_sql"), - #[cfg(feature = "query-sql")] EndpointType::BlazeAndSql => write!(f, "blaze_and_sql"), - #[cfg(feature = "query-sql")] EndpointType::Sql => write!(f, "sql"), } } @@ -190,12 +184,10 @@ struct CliArgs { exporter_api_key: Option, /// Postgres connection string - #[cfg(feature = "query-sql")] #[clap(long, env, value_parser)] postgres_connection_string: Option, /// Max number of attempts to connect to the database - #[cfg(feature = "query-sql")] #[clap(long, env, value_parser, default_value = "8")] max_db_attempts: u32, } @@ -229,9 +221,7 @@ pub(crate) struct Config { pub provider_icon: Option, pub auth_header: Option, pub exporter_api_key: Option, - #[cfg(feature = "query-sql")] pub postgres_connection_string: Option, - #[cfg(feature = "query-sql")] pub max_db_attempts: u32, } @@ -256,7 +246,6 @@ impl Config { api_key: cli_args.api_key, retry_count: cli_args.retry_count, endpoint_url: match cli_args.endpoint_type{ - #[cfg(feature = "query-sql")] EndpointType::Sql|EndpointType::EucaimSql => {Url::parse("http://localhost").unwrap()} // dummy value, never used, only Postgres connection string is used in those cases _ => { cli_args.endpoint_url.unwrap_or_else(|| cli_args.blaze_url.expect("Look, mate, you need to set endpoint-url or blaze-url, can't work without, sry")) @@ -284,9 +273,7 @@ impl Config { provider_icon: cli_args.provider_icon, auth_header: cli_args.auth_header, exporter_api_key: cli_args.exporter_api_key, - #[cfg(feature = "query-sql")] postgres_connection_string: cli_args.postgres_connection_string, - #[cfg(feature = "query-sql")] max_db_attempts: cli_args.max_db_attempts, client, }; diff --git a/src/errors.rs b/src/errors.rs index d79cab7..c5a2cbc 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -73,7 +73,6 @@ pub enum FocusError { QueryNotAllowed(String), #[error("CQL lang not enabled")] CqlLangNotEnabled, - #[cfg(feature = "query-sql")] #[error("Error executing SQL query: {0}")] ErrorExecutingSqlQuery(sqlx::Error), #[error("Unknown project: {0}")] diff --git a/src/flavours/bbmri/mod.rs b/src/flavours/bbmri/mod.rs index 02054ee..ad569a0 100644 --- a/src/flavours/bbmri/mod.rs +++ b/src/flavours/bbmri/mod.rs @@ -57,7 +57,7 @@ pub static CQL_SNIPPETS: LazyLock define function DiagnosisCode(condition FHIR.Condition): -condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() - -define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen): Coalesce( condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first(), condition.code.coding.where(system = 'http://fhir.de/CodeSystem/dimdi/icd-10-gm').code.first(), - condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first(), - specimen.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code.first() - ) + condition.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() +) define InInitialPopulation: {{retrieval_criteria}} \ No newline at end of file diff --git a/src/flavours/pscc/body.json b/src/flavours/pscc/body.json index 0b3eafb..6c63978 100644 --- a/src/flavours/pscc/body.json +++ b/src/flavours/pscc/body.json @@ -260,44 +260,6 @@ } } ] - }, - { - "code": { - "text": "Encounters" - }, - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis", - "valueCode": "Encounter" - } - ], - "population": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population" - } - ] - }, - "criteria": { - "language": "text/cql-identifier", - "expression": "Encounter" - } - } - ], - "stratifier": [ - { - "code": { - "text": "Departments" - }, - "criteria": { - "language": "text/cql", - "expression": "Departments" - } - } - ] } ] } diff --git a/src/flavours/pscc/template.cql b/src/flavours/pscc/template.cql index e008622..54df5cf 100644 --- a/src/flavours/pscc/template.cql +++ b/src/flavours/pscc/template.cql @@ -9,40 +9,46 @@ context Patient define Gender: if (Patient.gender is null) then 'unknown' else Patient.gender +define PrimaryDiagnosis: +First( +from [Condition] C +where C.extension.where(url='http://hl7.org/fhir/StructureDefinition/condition-related').empty()) + define AgeClass: -ToString((AgeInYears() div 10) * 10) +if (PrimaryDiagnosis.onset is null) then 'unknown' else ToString((AgeInYearsAt(FHIRHelpers.ToDateTime(PrimaryDiagnosis.onset)) div 10) * 10) define PatientDeceased: First (from [Observation: Code '75186-7' from loinc] O return O.value.coding.where(system = 'https://simplifier.net/PSCC/ValueSet/Vitalstatus').code.first()) + define Deceased: if (PatientDeceased is null) then 'unbekannt' else PatientDeceased define Diagnosis: if InInitialPopulation then [Condition] else {} as List + define function DiagnosisCode(condition FHIR.Condition): condition.code.coding.where(system = 'http://hl7.org/fhir/sid/icd-10').code.first() define Specimen: if InInitialPopulation then [Specimen] else {} as List + define function SampleType(specimen FHIR.Specimen): specimen.type.coding.where(system = 'https://pscc.org/fhir/CodeSystem/SampleMaterialType').code.first() + define function SampleSubtype(specimen FHIR.Specimen): specimen.type.text.first() define Procedure: if InInitialPopulation then [Procedure] else {} as List + define function ProcedureType(procedure FHIR.Procedure): -procedure.category.coding.where(system = 'http://dktk.dkfz.de/fhir/onco/core/CodeSystem/SYSTTherapieartCS').code.first() +procedure.category.coding.where(system = 'http://pscc.org/fhir/therapy').code.first() define MedicationStatement: if InInitialPopulation then [MedicationStatement] else {} as List + define function AppliedMedications(medication FHIR.MedicationStatement): medication.medication.coding.code.last() -define Encounter: -if InInitialPopulation then [Encounter] else {} as List -define function Departments(encounter FHIR.Encounter): -encounter.identifier.where(system = 'http://dktk.dkfz.de/fhir/sid/hki-department').value.first() - define InInitialPopulation: {{retrieval_criteria}} diff --git a/src/main.rs b/src/main.rs index d250abf..c00c833 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,10 @@ mod errors; mod graceful_shutdown; mod logger; +mod db; mod eucaim_api; mod eucaim_beacon; +mod eucaim_sql; mod exporter; mod flavours; mod intermediate_rep; @@ -17,14 +19,6 @@ mod mr; mod task_processing; mod transformed; mod util; - -#[cfg(feature = "query-sql")] -mod db; - -#[cfg(feature = "query-sql")] -mod eucaim_sql; - -#[cfg(feature = "query-sql")] use sqlx::Row; use base64::engine::general_purpose; @@ -198,18 +192,8 @@ pub async fn main() -> ExitCode { } } -#[cfg(not(feature = "query-sql"))] -type DbPool = (); - -#[cfg(feature = "query-sql")] type DbPool = sqlx::PgPool; -#[cfg(not(feature = "query-sql"))] -async fn get_db_pool() -> Result, ExitCode> { - Ok(None) -} - -#[cfg(feature = "query-sql")] async fn get_db_pool() -> Result, ExitCode> { use tracing::info; @@ -242,11 +226,8 @@ async fn main_loop() -> ExitCode { EndpointType::Omop | EndpointType::EucaimApi | EndpointType::EucaimBeacon => { || async { true }.boxed() } // TODO health check - #[cfg(feature = "query-sql")] EndpointType::EucaimSql => || async { true }.boxed(), - #[cfg(feature = "query-sql")] EndpointType::BlazeAndSql => || blaze::check_availability().boxed(), - #[cfg(feature = "query-sql")] EndpointType::Sql => || async { true }.boxed(), }; let mut failures = 0; @@ -353,7 +334,6 @@ async fn process_task( ) .await } - #[cfg(feature = "query-sql")] EndpointType::BlazeAndSql => { let mut generated_from_ast: bool = false; let data = base64_decode(&task.body)?; @@ -400,7 +380,6 @@ async fn process_task( } } } - #[cfg(feature = "query-sql")] EndpointType::Sql => { let data = base64_decode(&task.body)?; let query_maybe: Result = @@ -465,7 +444,6 @@ async fn process_task( Ok(run_eucaim_api_query(task, ast).await?) } - #[cfg(feature = "query-sql")] EndpointType::EucaimSql => { let decoded = util::base64_decode(&task.body)?; let intermediate_rep_query: intermediate_rep::IntermediateRepQuery = @@ -504,7 +482,6 @@ async fn process_task( } } -#[cfg(feature = "query-sql")] async fn run_eucaim_sql_query( task: &TaskRequest, pool: sqlx::Pool, @@ -591,7 +568,6 @@ async fn run_eucaim_sql_query( } } -#[cfg(feature = "query-sql")] async fn run_sql_key_query( task: &TaskRequest, pool: sqlx::Pool, diff --git a/src/util.rs b/src/util.rs index e06f30a..5c1f88c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -348,9 +348,6 @@ mod test { use super::*; use serde_json::json; - const QUERY_BBMRI_PLACEHOLDERS: &str = - include_str!("../resources/test/query_bbmri_placeholders.cql"); - const QUERY_BBMRI: &str = include_str!("../resources/test/query_bbmri.cql"); const EXAMPLE_MEASURE_REPORT_BBMRI: &str = include_str!("../resources/test/measure_report_bbmri.json"); const EXAMPLE_MEASURE_REPORT_BBMRI_NEW_EXTENSION: &str =