diff --git a/packages/demo/src/backends/ast-to-cql-translator.ts b/packages/demo/src/backends/ast-to-cql-translator.ts deleted file mode 100644 index 6b6a8148..00000000 --- a/packages/demo/src/backends/ast-to-cql-translator.ts +++ /dev/null @@ -1,380 +0,0 @@ -/** - * TODO: Document this file. Move to Project - */ - -import type { - AstBottomLayerValue, - AstElement, - AstTopLayer, - MeasureItem, -} from "../../../../dist/types"; -import { - alias as aliasMap, - cqltemplate, - criterionMap, -} from "./cqlquery-mappings"; - -let codesystems: string[] = []; -let criteria: string[]; - -export const translateAstToCql = ( - query: AstTopLayer, - returnOnlySingeltons: boolean = true, - backendMeasures: string, - measures: MeasureItem[], - criterionList: string[], -): string => { - criteria = criterionList; - - /** - * DISCUSS: why is this even an array? - * in bbmri there is only concatted to the string - */ - codesystems = [ - // NOTE: We always need loinc, as the Deceased Stratifier is computed with it!!! - "codesystem loinc: 'http://loinc.org'", - ]; - - const cqlHeader = - "library Retrieve\n" + - "using FHIR version '4.0.0'\n" + - "include FHIRHelpers version '4.0.0'\n" + - "\n"; - - let singletons: string = ""; - singletons = backendMeasures; - singletons += resolveOperation(query); - - if (query.children.length == 0) { - singletons += "\ntrue"; - } - - if (returnOnlySingeltons) { - return singletons; - } - - return ( - cqlHeader + - getCodesystems() + - "context Patient\n" + - measures.map((measureItem: MeasureItem) => measureItem.cql).join("") + - singletons - ); -}; - -const resolveOperation = (operation: AstElement): string => { - let expression: string = ""; - - if ("children" in operation && operation.children.length > 1) { - expression += "("; - } - - "children" in operation && - operation.children.forEach((element: AstElement, index) => { - if ("children" in element) { - expression += resolveOperation(element); - } - if ( - "key" in element && - "type" in element && - "system" in element && - "value" in element - ) { - expression += getSingleton(element); - } - if (index < operation.children.length - 1) { - expression += - ")" + ` ${operation.operand.toLowerCase()} ` + "\n("; - } else { - if (operation.children.length > 1) { - expression += ")"; - } - } - }); - - return expression; -}; - -const getSingleton = (criterion: AstBottomLayerValue): string => { - let expression: string = ""; - - //TODO: Workaround for using the value of "Therapy of Tumor" as key. Need an additional field in catalogue - if (criterion.key === "therapy_of_tumor") { - criterion.key = criterion.value as string; - } - - const myCriterion = criterionMap.get(criterion.key); - - if (myCriterion) { - const myCQL = cqltemplate.get(myCriterion.type); - if (myCQL) { - switch (myCriterion.type) { - case "gender": - case "BBMRI_gender": - case "histology": - case "conditionValue": - case "BBMRI_conditionValue": - case "BBMRI_conditionSampleDiagnosis": - case "conditionBodySite": - case "conditionLocalization": - case "observation": - case "uiccstadium": - case "observationMetastasis": - case "observationMetastasisBodySite": - case "procedure": - case "procedureResidualstatus": - case "medicationStatement": - case "specimen": - case "BBMRI_specimen": - case "BBMRI_hasSpecimen": - case "hasSpecimen": - case "Organization": - case "observationMolecularMarkerName": - case "observationMolecularMarkerAminoacidchange": - case "observationMolecularMarkerDNAchange": - case "observationMolecularMarkerSeqRefNCBI": - case "observationMolecularMarkerEnsemblID": - case "department": - case "TNMp": - case "TNMc": { - if (typeof criterion.value === "string") { - // TODO: Check if we really need to do this or we can somehow tell cql to do that expansion it self - if ( - criterion.value.slice(-1) === "%" && - criterion.value.length == 5 - ) { - const mykey = criterion.value.slice(0, -2); - if (criteria != undefined) { - const expandedValues = criteria.filter( - (value) => value.startsWith(mykey), - ); - expression += getSingleton({ - key: criterion.key, - type: criterion.type, - system: criterion.system, - value: expandedValues, - }); - } - } else if ( - criterion.value.slice(-1) === "%" && - criterion.value.length == 6 - ) { - const mykey = criterion.value.slice(0, -1); - if (criteria != undefined) { - const expandedValues = criteria.filter( - (value) => value.startsWith(mykey), - ); - expandedValues.push( - criterion.value.slice(0, 5), - ); - expression += getSingleton({ - key: criterion.key, - type: criterion.type, - system: criterion.system, - value: expandedValues, - }); - } - } else { - expression += substituteCQLExpression( - criterion.key, - myCriterion.alias, - myCQL, - criterion.value as string, - ); - } - } - if (typeof criterion.value === "boolean") { - expression += substituteCQLExpression( - criterion.key, - myCriterion.alias, - myCQL, - ); - } - - if (criterion.value instanceof Array) { - if (criterion.value.length === 1) { - expression += substituteCQLExpression( - criterion.key, - myCriterion.alias, - myCQL, - criterion.value[0], - ); - } else { - criterion.value.forEach((value: string) => { - expression += - "(" + - substituteCQLExpression( - criterion.key, - myCriterion.alias, - myCQL, - value, - ) + - ") or\n"; - }); - expression = expression.slice(0, -4); - } - } - - break; - } - - case "conditionRangeDate": { - expression += substituteRangeCQLExpression( - criterion, - myCriterion, - "condition", - "Date", - myCQL, - ); - break; - } - - case "primaryConditionRangeDate": { - expression += substituteRangeCQLExpression( - criterion, - myCriterion, - "primaryCondition", - "Date", - myCQL, - ); - break; - } - - case "conditionRangeAge": { - expression += substituteRangeCQLExpression( - criterion, - myCriterion, - "condition", - "Age", - myCQL, - ); - break; - } - - case "primaryConditionRangeAge": { - expression += substituteRangeCQLExpression( - criterion, - myCriterion, - "primaryCondition", - "Age", - myCQL, - ); - break; - } - } - } - } - return expression; -}; - -const substituteRangeCQLExpression = ( - criterion: AstBottomLayerValue, - myCriterion: { type: string; alias?: string[] }, - criterionPrefix: string, - criterionSuffix: string, - rangeCQL: string, -): string => { - const input = criterion.value as { min: number; max: number }; - if (input === null) { - console.warn( - `Throwing away a ${criterionPrefix}Range${criterionSuffix} criterion, as it is not of type {min: number, max: number}!`, - ); - return ""; - } - if (input.min === 0 && input.max === 0) { - console.warn( - `Throwing away a ${criterionPrefix}Range${criterionSuffix} criterion, as both dates are undefined!`, - ); - return ""; - } else if (input.min === 0) { - const lowerThanDateTemplate = cqltemplate.get( - `${criterionPrefix}LowerThan${criterionSuffix}`, - ); - if (lowerThanDateTemplate) - return substituteCQLExpression( - criterion.key, - myCriterion.alias, - lowerThanDateTemplate, - "", - input.min, - input.max, - ); - } else if (input.max === 0) { - const greaterThanDateTemplate = cqltemplate.get( - `${criterionPrefix}GreaterThan${criterionSuffix}`, - ); - if (greaterThanDateTemplate) - return substituteCQLExpression( - criterion.key, - myCriterion.alias, - greaterThanDateTemplate, - "", - input.min, - input.max, - ); - } else { - return substituteCQLExpression( - criterion.key, - myCriterion.alias, - rangeCQL, - "", - input.min, - input.max, - ); - } - return ""; -}; - -const substituteCQLExpression = ( - key: string, - alias: string[] | undefined, - cql: string, - value?: string, - min?: number, - max?: number, -): string => { - let cqlString: string; - if (value) { - cqlString = cql.replace(/{{C}}/g, value); - } else { - cqlString = cql; - } - cqlString = cqlString.replace(new RegExp("{{K}}"), key); - if (alias && alias[0]) { - cqlString = cqlString.replace(new RegExp("{{A1}}", "g"), alias[0]); - const systemExpression = - "codesystem " + alias[0] + ": '" + aliasMap.get(alias[0]) + "'"; - if (!codesystems.includes(systemExpression)) { - codesystems.push(systemExpression); - } - } - if (alias && alias[1]) { - cqlString = cqlString.replace(new RegExp("{{A2}}", "g"), alias[1]); - const systemExpression = - "codesystem " + alias[1] + ": '" + aliasMap.get(alias[1]) + "'"; - if (!codesystems.includes(systemExpression)) { - codesystems.push(systemExpression); - } - } - if (min || min === 0) { - cqlString = cqlString.replace(new RegExp("{{D1}}"), min.toString()); - } - if (max || max === 0) { - cqlString = cqlString.replace(new RegExp("{{D2}}"), max.toString()); - } - return cqlString; -}; - -const getCodesystems = (): string => { - let codesystemString: string = ""; - - codesystems.forEach((systems) => { - codesystemString += systems + "\n"; - }); - - if (codesystems.length > 0) { - codesystemString += "\n"; - } - - return codesystemString; -}; diff --git a/packages/demo/src/backends/blaze.ts b/packages/demo/src/backends/blaze.ts deleted file mode 100644 index a475acd7..00000000 --- a/packages/demo/src/backends/blaze.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { buildLibrary, buildMeasure } from "../helpers/cql-measure"; -import { responseStore } from "../stores/response"; -import type { Site } from "../types/response"; -import { measureStore } from "../stores/measures"; - -let measureDefinitions; - -measureStore.subscribe((store) => { - measureDefinitions = store.map((measure) => measure.measure); -}); - -export class Blaze { - constructor( - private url: URL, - private name: string, - private auth: string = "", - ) {} - - /** - * sends the query to beam and updates the store with the results - * @param cql the query as cql string - * @param controller the abort controller to cancel the request - */ - async send(cql: string, controller?: AbortController): Promise { - try { - responseStore.update((store) => { - store.set(this.name, { status: "claimed", data: null }); - return store; - }); - const libraryResponse = await fetch( - new URL(`${this.url}/Library`), - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(buildLibrary(cql)), - signal: controller?.signal, - }, - ); - if (!libraryResponse.ok) { - this.handleError( - `Couldn't create Library in Blaze`, - libraryResponse, - ); - } - const library = await libraryResponse.json(); - const measureResponse = await fetch( - new URL(`${this.url}/Measure`), - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify( - buildMeasure(library.url, measureDefinitions), - ), - signal: controller.signal, - }, - ); - if (!measureResponse.ok) { - this.handleError( - `Couldn't create Measure in Blaze`, - measureResponse, - ); - } - const measure = await measureResponse.json(); - const dataResponse = await fetch( - new URL( - `${this.url}/Measure/$evaluate-measure?measure=${measure.url}&periodStart=2000&periodEnd=2030`, - ), - { - signal: controller.signal, - }, - ); - if (!dataResponse.ok) { - this.handleError( - `Couldn't evaluate Measure in Blaze`, - dataResponse, - ); - } - const blazeResponse: Site = await dataResponse.json(); - responseStore.update((store) => { - store.set(this.name, { - status: "succeeded", - data: blazeResponse, - }); - return store; - }); - } catch (err) { - if (err.name === "AbortError") { - console.log(`Aborting former blaze request.`); - } else { - console.error(err); - } - } - } - - async handleError(message: string, response: Response): Promise { - const errorMessage = await response.text(); - console.debug( - `${message}. Received error ${response.status} with message ${errorMessage}`, - ); - responseStore.update((store) => { - store.set(this.name, { status: "permfailed", data: null }); - return store; - }); - } -} diff --git a/packages/demo/src/backends/cql-measure.ts b/packages/demo/src/backends/cql-measure.ts deleted file mode 100644 index 21f3b59a..00000000 --- a/packages/demo/src/backends/cql-measure.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; -import type { Measure } from "../types/backend"; - -type BuildLibraryReturn = { - resourceType: string; - url: string; - status: string; - type: { - coding: { - system: string; - code: string; - }[]; - }; - content: { - contentType: string; - data: string; - }[]; -}; - -export const buildLibrary = (cql: string): BuildLibraryReturn => { - const libraryId = uuidv4(); - const encodedQuery = btoa(unescape(encodeURIComponent(cql))); - return { - resourceType: "Library", - url: "urn:uuid:" + libraryId, - status: "active", - type: { - coding: [ - { - system: "http://terminology.hl7.org/CodeSystem/library-type", - code: "logic-library", - }, - ], - }, - content: [ - { - contentType: "text/cql", - data: encodedQuery, - }, - ], - }; -}; - -type BuildMeasureReturn = { - resourceType: string; - url: string; - status: string; - subjectCodeableConcept: { - coding: { - system: string; - code: string; - }[]; - }; - library: string; - scoring: { - coding: { - system: string; - code: string; - }[]; - }; - group: Measure[]; -}; - -export const buildMeasure = ( - libraryUrl: string, - measures: Measure[], -): BuildMeasureReturn => { - const measureId = uuidv4(); - return { - resourceType: "Measure", - url: "urn:uuid:" + measureId, - status: "active", - subjectCodeableConcept: { - coding: [ - { - system: "http://hl7.org/fhir/resource-types", - code: "Patient", - }, - ], - }, - library: libraryUrl, - scoring: { - coding: [ - { - system: "http://terminology.hl7.org/CodeSystem/measure-scoring", - code: "cohort", - }, - ], - }, - group: measures, // configuration.resultRequests.map(request => request.measures) - }; -}; diff --git a/packages/demo/src/backends/cqlquery-mappings.ts b/packages/demo/src/backends/cqlquery-mappings.ts deleted file mode 100644 index 12bcac63..00000000 --- a/packages/demo/src/backends/cqlquery-mappings.ts +++ /dev/null @@ -1,429 +0,0 @@ -export const alias = new Map([ - ["icd10", "http://fhir.de/CodeSystem/bfarm/icd-10-gm"], - ["loinc", "http://loinc.org"], - ["gradingcs", "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/GradingCS"], - ["ops", "http://fhir.de/CodeSystem/bfarm/ops"], - ["morph", "urn:oid:2.16.840.1.113883.6.43.1"], - ["lokalisation_icd_o_3", "urn:oid:2.16.840.1.113883.6.43.1"], - [ - "bodySite", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/SeitenlokalisationCS", - ], - [ - "Therapieart", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/SYSTTherapieartCS", - ], - ["specimentype", "https://fhir.bbmri.de/CodeSystem/SampleMaterialType"], - [ - "uiccstadiumcs", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/UiccstadiumCS", - ], - [ - "lokalebeurteilungresidualstatuscs", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/LokaleBeurteilungResidualstatusCS", - ], - [ - "gesamtbeurteilungtumorstatuscs", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/GesamtbeurteilungTumorstatusCS", - ], - [ - "verlauflokalertumorstatuscs", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/VerlaufLokalerTumorstatusCS", - ], - [ - "verlauftumorstatuslymphknotencs", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/VerlaufTumorstatusLymphknotenCS", - ], - [ - "verlauftumorstatusfernmetastasencs", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/VerlaufTumorstatusFernmetastasenCS", - ], - [ - "vitalstatuscs", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/VitalstatusCS", - ], - ["jnucs", "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/JNUCS"], - [ - "fmlokalisationcs", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/FMLokalisationCS", - ], - ["TNMTCS", "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/TNMTCS"], - ["TNMNCS", "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/TNMNCS"], - ["TNMMCS", "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/TNMMCS"], - [ - "TNMySymbolCS", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/TNMySymbolCS", - ], - [ - "TNMrSymbolCS", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/TNMrSymbolCS", - ], - [ - "TNMmSymbolCS", - "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/TNMmSymbolCS", - ], - ["molecularMarker", "http://www.genenames.org"], - - ["BBMRI_icd10", "http://hl7.org/fhir/sid/icd-10"], - ["BBMRI_icd10gm", "http://fhir.de/CodeSystem/dimdi/icd-10-gm"], - [ - "BBMRI_SampleMaterialType", - "https://fhir.bbmri.de/CodeSystem/SampleMaterialType", - ], //specimentype - [ - "BBMRI_StorageTemperature", - "https://fhir.bbmri.de/CodeSystem/StorageTemperature", - ], - [ - "BBMRI_SmokingStatus", - "http://hl7.org/fhir/uv/ips/ValueSet/current-smoking-status-uv-ips", - ], -]); - -export const cqltemplate = new Map([ - ["gender", "Patient.gender = '{{C}}'"], - ["conditionValue", "exists [Condition: Code '{{C}}' from {{A1}}]"], - [ - "conditionBodySite", - "exists from [Condition] C\nwhere C.bodySite.coding contains Code '{{C}}' from {{A1}}", - ], - //TODO Revert to first expression if https://github.com/samply/blaze/issues/808 is solved - // ["conditionLocalization", "exists from [Condition] C\nwhere C.bodySite.coding contains Code '{{C}}' from {{A1}}"], - [ - "conditionLocalization", - "exists from [Condition] C\nwhere C.bodySite.coding.code contains '{{C}}'", - ], - [ - "conditionRangeDate", - "exists from [Condition] C\nwhere year from C.onset between {{D1}} and {{D2}}", - ], - [ - "conditionLowerThanDate", - "exists from [Condition] C\nwhere year from C.onset <= {{D2}}", - ], - [ - "conditionGreaterThanDate", - "exists from [Condition] C\nwhere year from C.onset >= {{D1}}", - ], - [ - "conditionRangeAge", - "exists [Condition] C\nwhere AgeInYearsAt(FHIRHelpers.ToDateTime(C.onset)) between {{D1}} and {{D2}}", - ], - [ - "conditionLowerThanAge", - "exists [Condition] C\nwhere AgeInYearsAt(FHIRHelpers.ToDateTime(C.onset)) <= {{D2}}", - ], - [ - "conditionGreaterThanAge", - "exists [Condition] C\nwhere AgeInYearsAt(FHIRHelpers.ToDateTime(C.onset)) >= {{D1}}", - ], - [ - "primaryConditionRangeDate", - "year from PrimaryDiagnosis.onset between {{D1}} and {{D2}}", - ], - [ - "primaryConditionLowerThanDate", - "year from PrimaryDiagnosis.onset <= {{D2}}", - ], - [ - "primaryConditionGreaterThanDate", - "year from PrimaryDiagnosis.onset >= {{D1}}", - ], - [ - "primaryConditionRangeAge", - "AgeInYearsAt(FHIRHelpers.ToDateTime(PrimaryDiagnosis.onset)) between {{D1}} and {{D2}}", - ], - [ - "primaryConditionLowerThanAge", - "AgeInYearsAt(FHIRHelpers.ToDateTime(PrimaryDiagnosis.onset)) <= {{D2}}", - ], - [ - "primaryConditionGreaterThanAge", - "AgeInYearsAt(FHIRHelpers.ToDateTime(PrimaryDiagnosis.onset)) >= {{D1}}", - ], - //TODO Revert to first expression if https://github.com/samply/blaze/issues/808 is solved - // ["observation", "exists from [Observation: Code '{{K}}' from {{A1}}] O\nwhere O.value.coding contains Code '{{C}}' from {{A2}}"], - [ - "observation", - "exists from [Observation: Code '{{K}}' from {{A1}}] O\nwhere O.value.coding.code contains '{{C}}'", - ], - [ - "observationMetastasis", - "exists from [Observation: Code '21907-1' from {{A1}}] O\nwhere O.value.coding.code contains '{{C}}'", - ], - [ - "observationMetastasisBodySite", - "exists from [Observation: Code '21907-1' from {{A1}}] O\nwhere O.bodySite.coding.code contains '{{C}}'", - ], - [ - "observationMolecularMarkerName", - "exists from [Observation: Code '69548-6' from {{A1}}] O\nwhere O.component.where(code.coding contains Code '{{K}}' from {{A1}}).value.coding contains Code '{{C}}' from {{A2}}", - ], - [ - "observationMolecularMarkerAminoacidchange", - "exists from [Observation: Code '69548-6' from {{A1}}] O\nwhere O.component.where(code.coding contains Code '{{K}}' from {{A1}}).value = '{{C}}'", - ], //TODO @ThomasK replace C with S - [ - "observationMolecularMarkerDNAchange", - "exists from [Observation: Code '69548-6' from {{A1}}] O\nwhere O.component.where(code.coding contains Code '{{K}}' from {{A1}}).value = '{{C}}'", - ], - [ - "observationMolecularMarkerSeqRefNCBI", - "exists from [Observation: Code '69548-6' from {{A1}}] O\nwhere O.component.where(code.coding contains Code '{{K}}' from {{A1}}).value = '{{C}}'", - ], - [ - "observationMolecularMarkerEnsemblID", - "exists from [Observation: Code '69548-6' from {{A1}}] O\nwhere O.component.where(code.coding contains Code '{{K}}' from {{A1}}).value = '{{C}}'", - ], - ["procedure", "exists [Procedure: category in Code '{{K}}' from {{A1}}]"], - [ - "procedureResidualstatus", - "exists from [Procedure: category in Code 'OP' from {{A1}}] P\nwhere P.outcome.coding.code contains '{{C}}'", - ], - [ - "medicationStatement", - "exists [MedicationStatement: category in Code '{{K}}' from {{A1}}]", - ], - ["hasSpecimen", "exists [Specimen]"], - ["specimen", "exists [Specimen: Code '{{C}}' from {{A1}}]"], - [ - "TNMc", - "exists from [Observation: Code '21908-9' from {{A1}}] O\nwhere O.component.where(code.coding contains Code '{{K}}' from {{A1}}).value.coding contains Code '{{C}}' from {{A2}}", - ], - [ - "TNMp", - "exists from [Observation: Code '21902-2' from {{A1}}] O\nwhere O.component.where(code.coding contains Code '{{K}}' from {{A1}}).value.coding contains Code '{{C}}' from {{A2}}", - ], - [ - "Organization", - "Patient.managingOrganization.reference = \"Organization Ref\"('Klinisches Krebsregister/ITM')", - ], - [ - "department", - "exists from [Encounter] I\nwhere I.identifier.value = '{{C}}' ", - ], - [ - "uiccstadium", - "(exists ([Observation: Code '21908-9' from loinc] O where O.value.coding.code contains '{{C}}')) or (exists ([Observation: Code '21902-2' from loinc] O where O.value.coding.code contains '{{C}}'))", - ], - ["histology", "exists from [Observation: Code '59847-4' from loinc] O\n"], - - ["BBMRI_gender", "Patient.gender"], - [ - "BBMRI_conditionSampleDiagnosis", - "((exists[Condition: Code '{{C}}' from {{A1}}]) or (exists[Condition: Code '{{C}}' from {{A2}}])) or (exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/SampleDiagnosis').value.coding.code contains '{{C}}'))", - ], - ["BBMRI_conditionValue", "exists [Condition: Code '{{C}}' from {{A1}}]"], - [ - "BBMRI_conditionRangeDate", - "exists from [Condition] C\nwhere FHIRHelpers.ToDateTime(C.onset) between {{D1}} and {{D2}}", - ], - [ - "BBMRI_conditionRangeAge", - "exists from [Condition] C\nwhere AgeInYearsAt(FHIRHelpers.ToDateTime(C.onset)) between Ceiling({{D1}}) and Ceiling({{D2}})", - ], - ["BBMRI_age", "AgeInYears() between Ceiling({{D1}}) and Ceiling({{D2}})"], - [ - "BBMRI_observation", - "exists from [Observation: Code '{{K}}' from {{A1}}] O\nwhere O.value.coding.code contains '{{C}}'", - ], - [ - "BBMRI_observationSmoker", - "exists from [Observation: Code '72166-2' from {{A1}}] O\nwhere O.value.coding.code contains '{{C}}'", - ], - [ - "BBMRI_observationRange", - "exists from [Observation: Code '{{K}}' from {{A1}}] O\nwhere O.value between {{D1}} and {{D2}}", - ], - [ - "BBMRI_observationBodyWeight", - "exists from [Observation: Code '29463-7' from {{A1}}] O\nwhere ((O.value as Quantity) < {{D1}} 'kg' and (O.value as Quantity) > {{D2}} 'kg')", - ], - [ - "BBMRI_observationBMI", - "exists from [Observation: Code '39156-5' from {{A1}}] O\nwhere ((O.value as Quantity) < {{D1}} 'kg/m2' and (O.value as Quantity) > {{D2}} 'kg/m2')", - ], - ["BBMRI_hasSpecimen", "exists [Specimen]"], - ["BBMRI_specimen", "exists [Specimen: Code '{{C}}' from {{A1}}]"], - ["BBMRI_retrieveSpecimenByType", "(S.type.coding.code contains '{{C}}')"], - [ - "BBMRI_retrieveSpecimenByTemperature", - "(S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/StorageTemperature').value.coding.code contains '{{C}}')", - ], - [ - "BBMRI_retrieveSpecimenBySamplingDate", - "(FHIRHelpers.ToDateTime(S.collection.collected) between {{D1}} and {{D2}})", - ], - [ - "BBMRI_retrieveSpecimenByFastingStatus", - "(S.collection.fastingStatus.coding.code contains '{{C}}')", - ], - [ - "BBMRI_samplingDate", - "exists from [Specimen] S\nwhere FHIRHelpers.ToDateTime(S.collection.collected) between {{D1}} and {{D2}}", - ], - [ - "BBMRI_fastingStatus", - "exists from [Specimen] S\nwhere S.collection.fastingStatus.coding.code contains '{{C}}'", - ], - [ - "BBMRI_storageTemperature", - "exists from [Specimen] S where (S.extension.where(url='https://fhir.bbmri.de/StructureDefinition/StorageTemperature').value.coding contains Code '{{C}}' from {{A1}})", - ], -]); - -export const criterionMap = new Map( - [ - ["gender", { type: "gender" }], - ["histology", { type: "histology", alias: ["loinc"] }], - ["diagnosis", { type: "conditionValue", alias: ["icd10"] }], - ["bodySite", { type: "conditionBodySite", alias: ["bodySite"] }], - [ - "urn:oid:2.16.840.1.113883.6.43.1", - { type: "conditionLocalization", alias: ["lokalisation_icd_o_3"] }, - ], - ["59542-1", { type: "observation", alias: ["loinc", "gradingcs"] }], //grading - [ - "metastases_present", - { type: "observationMetastasis", alias: ["loinc", "jnucs"] }, - ], //Fernmetastasen vorhanden - [ - "localization_metastases", - { - type: "observationMetastasisBodySite", - alias: ["loinc", "fmlokalisationcs"], - }, - ], //Fernmetastasen - ["OP", { type: "procedure", alias: ["Therapieart"] }], //Operation - ["ST", { type: "procedure", alias: ["Therapieart"] }], //Strahlentherapie - ["CH", { type: "medicationStatement", alias: ["Therapieart"] }], //Chemotherapie - ["HO", { type: "medicationStatement", alias: ["Therapieart"] }], //Hormontherapie - ["IM", { type: "medicationStatement", alias: ["Therapieart"] }], //Immuntherapie - ["KM", { type: "medicationStatement", alias: ["Therapieart"] }], //Knochenmarktransplantation - ["59847-4", { type: "observation", alias: ["loinc", "morph"] }], //Morphologie - ["year_of_diagnosis", { type: "conditionRangeDate" }], - ["year_of_primary_diagnosis", { type: "primaryConditionRangeDate" }], - ["sample_kind", { type: "specimen", alias: ["specimentype"] }], - ["pat_with_samples", { type: "hasSpecimen" }], - ["age_at_diagnosis", { type: "conditionRangeAge" }], - ["age_at_primary_diagnosis", { type: "primaryConditionRangeAge" }], - ["21908-9", { type: "uiccstadium", alias: ["loinc", "uiccstadiumcs"] }], - ["21905-5", { type: "TNMc", alias: ["loinc", "TNMTCS"] }], //tnm component - ["21906-3", { type: "TNMc", alias: ["loinc", "TNMNCS"] }], //tnm component - ["21907-1", { type: "TNMc", alias: ["loinc", "TNMMCS"] }], //tnm component - ["42030-7", { type: "TNMc", alias: ["loinc", "TNMmSymbolCS"] }], //tnm component - ["59479-6", { type: "TNMc", alias: ["loinc", "TNMySymbolCS"] }], //tnm component - ["21983-2", { type: "TNMc", alias: ["loinc", "TNMrSymbolCS"] }], //tnm component - ["21899-0", { type: "TNMp", alias: ["loinc", "TNMTCS"] }], //tnm component - ["21900-6", { type: "TNMp", alias: ["loinc", "TNMNCS"] }], //tnm component - ["21901-4", { type: "TNMp", alias: ["loinc", "TNMMCS"] }], //tnm component - ["42030-7", { type: "TNMp", alias: ["loinc", "TNMmSymbolCS"] }], //tnm component - ["59479-6", { type: "TNMp", alias: ["loinc", "TNMySymbolCS"] }], //tnm component - ["21983-2", { type: "TNMp", alias: ["loinc", "TNMrSymbolCS"] }], //tnm component - - ["Organization", { type: "Organization" }], //organization - [ - "48018-6", - { - type: "observationMolecularMarkerName", - alias: ["loinc", "molecularMarker"], - }, - ], //molecular marker name - [ - "48005-3", - { - type: "observationMolecularMarkerAminoacidchange", - alias: ["loinc"], - }, - ], //molecular marker - [ - "81290-9", - { type: "observationMolecularMarkerDNAchange", alias: ["loinc"] }, - ], //molecular marker - [ - "81248-7", - { type: "observationMolecularMarkerSeqRefNCBI", alias: ["loinc"] }, - ], //molecular marker - [ - "81249-5", - { type: "observationMolecularMarkerEnsemblID", alias: ["loinc"] }, - ], //molecular marker - - [ - "local_assessment_residual_tumor", - { - type: "procedureResidualstatus", - alias: ["Therapieart", "lokalebeurteilungresidualstatuscs"], - }, - ], //lokalebeurteilungresidualstatuscs - [ - "21976-6", - { - type: "observation", - alias: ["loinc", "gesamtbeurteilungtumorstatuscs"], - }, - ], //GesamtbeurteilungTumorstatus - [ - "LA4583-6", - { - type: "observation", - alias: ["loinc", "verlauflokalertumorstatuscs"], - }, - ], //LokalerTumorstatus - [ - "LA4370-8", - { - type: "observation", - alias: ["loinc", "verlauftumorstatuslymphknotencs"], - }, - ], //TumorstatusLymphknoten - [ - "LA4226-2", - { - type: "observation", - alias: ["loinc", "verlauftumorstatusfernmetastasencs"], - }, - ], //TumorstatusFernmetastasen - ["75186-7", { type: "observation", alias: ["loinc", "vitalstatuscs"] }], //Vitalstatus - //["Organization", {type: "Organization"}], - ["Organization", { type: "department" }], - - ["BBMRI_gender", { type: "BBMRI_gender" }], - [ - "BBMRI_diagnosis", - { - type: "BBMRI_conditionSampleDiagnosis", - alias: ["BBMRI_icd10", "BBMRI_icd10gm"], - }, - ], - [ - "BBMRI_body_weight", - { type: "BBMRI_observationBodyWeight", alias: ["loinc"] }, - ], //Body weight - ["BBMRI_bmi", { type: "BBMRI_observationBMI", alias: ["loinc"] }], //BMI - [ - "BBMRI_smoking_status", - { type: "BBMRI_observationSmoker", alias: ["loinc"] }, - ], //Smoking habit - ["BBMRI_donor_age", { type: "BBMRI_age" }], - ["BBMRI_date_of_diagnosis", { type: "BBMRI_conditionRangeDate" }], - [ - "BBMRI_sample_kind", - { type: "BBMRI_specimen", alias: ["BBMRI_SampleMaterialType"] }, - ], - [ - "BBMRI_storage_temperature", - { - type: "BBMRI_storageTemperature", - alias: ["BBMRI_StorageTemperature"], - }, - ], - ["BBMRI_pat_with_samples", { type: "BBMRI_hasSpecimen" }], - ["BBMRI_diagnosis_age_donor", { type: "BBMRI_conditionRangeAge" }], - [ - "BBMRI_fasting_status", - { type: "BBMRI_fastingStatus", alias: ["loinc"] }, - ], - ["BBMRI_sampling_date", { type: "BBMRI_samplingDate" }], - ], -); diff --git a/packages/demo/src/backends/spot.ts b/packages/demo/src/backends/spot.ts deleted file mode 100644 index fa1f0d38..00000000 --- a/packages/demo/src/backends/spot.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * TODO: document this class - */ - -import type { - ResponseStore, - SiteData, - Status, - BeamResult, -} from "../../../../dist/types"; - -export class Spot { - private currentTask!: string; - - constructor( - private url: URL, - private sites: Array, - ) {} - - /** - * sends the query to beam and updates the store with the results - * @param query the query as base64 encoded string - * @param updateResponse the function to update the response store - * @param controller the abort controller to cancel the request - */ - async send( - query: string, - updateResponse: (response: ResponseStore) => void, - controller: AbortController, - ): Promise { - try { - this.currentTask = crypto.randomUUID(); - const beamTaskResponse = await fetch( - `${this.url}beam?sites=${this.sites.toString()}`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - credentials: import.meta.env.PROD ? "include" : "omit", - body: JSON.stringify({ - id: this.currentTask, - sites: this.sites, - query: query, - }), - signal: controller.signal, - }, - ); - if (!beamTaskResponse.ok) { - const error = await beamTaskResponse.text(); - console.debug( - `Received ${beamTaskResponse.status} with message ${error}`, - ); - throw new Error(`Unable to create new beam task.`); - } - - console.info(`Created new Beam Task with id ${this.currentTask}`); - - const eventSource = new EventSource( - `${this.url.toString()}beam/${this.currentTask}?wait_count=${this.sites.length}`, - { - withCredentials: true, - }, - ); - - /** - * Listenes to the new_result event from beam and updates the response store - */ - eventSource.addEventListener("new_result", (message) => { - const response: BeamResult = JSON.parse(message.data); - if (response.task !== this.currentTask) return; - const site: string = response.from.split(".")[1]; - const status: Status = response.status; - const body: SiteData = - status === "succeeded" - ? JSON.parse(atob(response.body)) - : null; - - const parsedResponse: ResponseStore = new Map().set(site, { - status: status, - data: body, - }); - updateResponse(parsedResponse); - }); - - // read error events from beam - eventSource.addEventListener("error", (message) => { - console.error(`Beam returned error ${message}`); - eventSource.close(); - }); - - // event source in javascript throws an error then the event source is closed by backend - eventSource.onerror = () => { - console.info( - `Querying results from sites for task ${this.currentTask} finished.`, - ); - eventSource.close(); - }; - } catch (err) { - if (err instanceof Error && err.name === "AbortError") { - console.log(`Aborting request ${this.currentTask}`); - } else { - console.error(err); - } - } - } -}