Skip to content

Commit

Permalink
Merge pull request #133 from samply/feature/icd10gmnew
Browse files Browse the repository at this point in the history
added bfarm icd10gm
  • Loading branch information
enola-dkfz authored Apr 23, 2024
2 parents ce59cac + dd51f0d commit 408bfae
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 69 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ rand = { default-features = false, version = "0.8.5" }

[dev-dependencies]
tokio-test = "0.4.2"
pretty_assertions = "1.4.0"

[build-dependencies]
build-data = "0"
Expand Down
5 changes: 4 additions & 1 deletion resources/cql/BBMRI_STRAT_DIAGNOSIS_STRATIFIER
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
define Diagnosis:
if InInitialPopulation then [Condition] else {} as List<Condition>
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(), 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())
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())

5 changes: 4 additions & 1 deletion resources/test/query_bbmri.cql
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ define Specimen:
define Diagnosis:
if InInitialPopulation then [Condition] else {} as List<Condition>
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(), 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())
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())



Expand Down
85 changes: 18 additions & 67 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::errors::FocusError;
use base64::Engine as _;
use base64::engine::general_purpose;
use base64::Engine as _;
use laplace_rs::{get_from_cache_or_privatize, Bin, ObfCache, ObfuscateBelow10Mode};
use rand::thread_rng;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -350,7 +350,8 @@ mod test {
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_DKTK: &str = include_str!("../resources/test/measure_report_dktk.json");
const EXAMPLE_MEASURE_REPORT_DKTK: &str =
include_str!("../resources/test/measure_report_dktk.json");
const EXAMPLE_MEASURE_REPORT_EXLIQUID: &str =
include_str!("../resources/test/measure_report_exliquid.json");

Expand Down Expand Up @@ -387,7 +388,7 @@ mod test {
// Call the function and assert that it returns the expected result
let result = get_json_field(json_string, "address");
assert!(result.is_ok());
assert_eq!(result.unwrap(), expected_result);
pretty_assertions::assert_eq!(result.unwrap(), expected_result);
}

#[test]
Expand All @@ -408,7 +409,7 @@ mod test {
// Call the function and assert that it returns json null
let result = get_json_field(json_string, "phone");
assert!(result.is_ok());
assert_eq!(result.unwrap(), json!(null));
pretty_assertions::assert_eq!(result.unwrap(), json!(null));
}

#[test]
Expand All @@ -433,72 +434,22 @@ mod test {
assert!(!is_cql_tampered_with(decoded_library));
}

#[test]
fn test_replace_cql_all() {
for (decoded_library, expected_result) in REPLACE_MAP.iter() {
pretty_assertions::assert_eq!(replace_cql(*decoded_library).as_str(), *expected_result);
}
}

#[test]
fn test_replace_cql() {
let decoded_library = QUERY_BBMRI_PLACEHOLDERS;
let expected_result = QUERY_BBMRI;
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "BBMRI_STRAT_GENDER_STRATIFIER";
let expected_result = "define Gender:\n if (Patient.gender is null) then 'unknown'\n else if (Patient.gender != 'male' and Patient.gender != 'female' and Patient.gender != 'other' and Patient.gender != 'unknown') then 'other'\n else Patient.gender";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "BBMRI_STRAT_CUSTODIAN_STRATIFIER";
let expected_result = "define Custodian:\n First(from Specimen.extension E\n where E.url = 'https://fhir.bbmri.de/StructureDefinition/Custodian'\n return (E.value as Reference).identifier.value)\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "BBMRI_STRAT_DIAGNOSIS_STRATIFIER";
let expected_result = "define Diagnosis:\n if InInitialPopulation then [Condition] else {} as List<Condition> \n define function DiagnosisCode(condition FHIR.Condition, specimen FHIR.Specimen):\n 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(), 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())\n\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "BBMRI_STRAT_AGE_STRATIFIER";
let expected_result = "define AgeClass:\n (AgeInYears() div 10) * 10\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "BBMRI_STRAT_DEF_SPECIMEN";
let expected_result = "define Specimen:\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "BBMRI_STRAT_DEF_IN_INITIAL_POPULATION";
let expected_result = "define InInitialPopulation:\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "DKTK_STRAT_PRIMARY_DIAGNOSIS_STRATIFIER";
let expected_result = "define PrimaryDiagnosis:\nFirst(\nfrom [Condition] C\nwhere C.extension.where(url='http://hl7.org/fhir/StructureDefinition/condition-related').empty()\nsort by date from onset asc)\n\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "DKTK_STRAT_AGE_CLASS_STRATIFIER";
let expected_result = "define AgeClass:\nif (PrimaryDiagnosis.onset is null) then 'unknown' else ToString((AgeInYearsAt(FHIRHelpers.ToDateTime(PrimaryDiagnosis.onset)) div 10) * 10)\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "EXLIQUID_CQL_DIAGNOSIS";
let expected_result = "define retrieveCondition: First(from [Condition] C return C.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first())\ndefine Diagnosis: if (retrieveCondition is null) then 'unknown' else retrieveCondition\n\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "EXLIQUID_CQL_SPECIMEN";
let expected_result = "define Specimen:\nif InInitialPopulation then [Specimen] else {} as List<Specimen>\ndefine ExliquidSpecimen:\n from [Specimen] S\n where S.identifier.system contains 'http://dktk.dkfz.de/fhir/sid/exliquid-specimen'\ndefine function SampleType(specimen FHIR.Specimen):\n specimen.type.coding.where(system = 'https://fhir.bbmri.de/CodeSystem/SampleMaterialType').code.first()\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "EXLIQUID_STRAT_W_ALIQUOTS";
let expected_result = "define InInitialPopulation:\n exists AnySpecimen\n \ndefine AnySpecimen:\n [Specimen] S\n\ndefine retrieveCondition:\n First(from [Condition] C\n return ('{\\\"subject_reference\\\": \\\"' + C.subject.reference \n + '\\\", \\\"diagnosis_code\\\": \\\"' \n + C.code.coding.where(system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm').code.first() \n + '\\\"}'\n ))\n \ndefine Diagnosis:\n if (retrieveCondition is null) then '{\\\"subject_reference\\\": \\\"\\\", \\\"diagnosis_code\\\": \\\"\\\"}' \n else retrieveCondition\n\ndefine function getSampletype(specimen FHIR.Specimen):\n if (not exists specimen.type.coding.where(system = 'https://fhir.bbmri.de/CodeSystem/SampleMaterialType').code) then 'null'\n else specimen.type.coding.where(system = 'https://fhir.bbmri.de/CodeSystem/SampleMaterialType').code.first()\n\ndefine function getRestamount(specimen FHIR.Specimen):\n if (not exists specimen.collection.quantity.value) then '0' else specimen.collection.quantity.value.toString()\n\ndefine function getParentReference(specimen FHIR.Specimen): \n if (not exists specimen.parent.reference) then 'null' else specimen.parent.reference\n\ndefine function getSubjectReference(specimen FHIR.Specimen): \n if (not exists specimen.subject.reference) then 'null' else specimen.subject.reference\n\ndefine function SingleStrat(specimen FHIR.Specimen):\n '{\"specimen_id\": \"' + specimen.id + \n '\", \"sampletype\": \"' + getSampletype(specimen) +\n '\", \"exliquid_tag\": ' + (specimen.identifier.system contains 'http://dktk.dkfz.de/fhir/sid/exliquid-specimen').toString() +\n ', \"rest_amount\": \"' + getRestamount(specimen) +\n '\", \"parent_reference\": \"' + getParentReference(specimen) +\n '\", \"subject_reference\": \"' + getSubjectReference(specimen) +\n '\"}'";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "EXLIQUID_STRAT_DEF_IN_INITIAL_POPULATION";
let expected_result = "define InInitialPopulation:\n exists ExliquidSpecimen and\n\n";
assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "MTBA_STRAT_GENETIC_VARIANT";
let expected_result = "define GeneticVariantCode:\nFirst (from [Observation: Code '69548-6' from loinc] O return O.component.where(code.coding contains Code '48018-6' from loinc).value.coding.code.first())\n";

assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "DKTK_STRAT_HISTOLOGY_STRATIFIER";
let expected_result = "define Histo:\nif InInitialPopulation then [Observation] else {} as List <Observation>\n\ndefine function Histology(histo FHIR.Observation):\n if histo.code.coding.where(code = '59847-4').code.first() is null then 0 else 1\n";
assert_eq!(replace_cql(decoded_library), expected_result);
pretty_assertions::assert_eq!(replace_cql(decoded_library), expected_result);

let decoded_library = "INVALID_KEY";
let expected_result = "INVALID_KEY";
assert_eq!(replace_cql(decoded_library), expected_result);
pretty_assertions::assert_eq!(replace_cql(decoded_library), expected_result);
}

#[test]
Expand Down Expand Up @@ -544,7 +495,7 @@ mod test {
ROUNDING_STEP,
)
.unwrap();
assert_eq!(obfuscated_json, obfuscated_json_2);
pretty_assertions::assert_eq!(obfuscated_json, obfuscated_json_2);
}

#[test]
Expand Down Expand Up @@ -572,7 +523,7 @@ mod test {
let _: MeasureReport = serde_json::from_str(&obfuscated_json).unwrap();

// Check that the obfuscated JSON is different from the original JSON
assert_ne!(obfuscated_json, EXAMPLE_MEASURE_REPORT_DKTK);
pretty_assertions::assert_ne!(obfuscated_json, EXAMPLE_MEASURE_REPORT_DKTK);

// Check that obfuscating the same JSON twice with the same obfuscation cache gives the same result
let obfuscated_json_2 = obfuscate_counts_mr(
Expand All @@ -590,7 +541,7 @@ mod test {
ROUNDING_STEP,
)
.unwrap();
assert_eq!(obfuscated_json, obfuscated_json_2);
pretty_assertions::assert_eq!(obfuscated_json, obfuscated_json_2);
}

#[test]
Expand All @@ -613,7 +564,7 @@ mod test {
ROUNDING_STEP,
);

assert_eq!(
pretty_assertions::assert_eq!(
obfuscated_json.unwrap_err().to_string(),
r#"Deserialization error: missing field `text` at line 42 column 13. Is obfuscation turned on when it shouldn't be? Is the metadata in the task formatted correctly, like this {"project": "name"}? Are there any other projects stated in the projects_no_obfuscation parameter in the bridgehead?"#
);
Expand Down

0 comments on commit 408bfae

Please sign in to comment.