Skip to content

Commit

Permalink
Query struct (#2301)
Browse files Browse the repository at this point in the history
* Save draft

* Refactor single generalized query
  • Loading branch information
bamader authored Aug 2, 2024
1 parent 2241ca2 commit 7632e9f
Show file tree
Hide file tree
Showing 4 changed files with 271 additions and 297 deletions.
135 changes: 135 additions & 0 deletions containers/tefca-viewer/src/app/CustomQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* A Data Class designed to store and manipulate various code values used
* to create a fully customized FHIR query. The class holds instance variables
* for each of four types of input codes (lab LOINCs, SNOMED/conditions, RXNorm,
* and Class/System-Type); the class uses these to automatically compile query
* strings for each subtype of eCR query that can be built using those codes'
* information.
*/
export class CustomQuery {
// Store four types of input codes
labCodes: string[] = [];
snomedCodes: string[] = [];
rxnormCodes: string[] = [];
classTypeCodes: string[] = [];

// Need default initialization of query strings outstide constructor,
// since the `try` means we might not find the JSON spec
observationQuery: string = "";
diagnosticReportQuery: string = "";
conditionQuery: string = "";
medicationRequestQuery: string = "";
socialHistoryQuery: string = "";
encounterQuery: string = "";
encounterClassTypeQuery: string = "";

// Some queries need to be batched in waves because their encounter references
// might depend on demographic information
hasSecondEncounterQuery: boolean = false;

/**
* Creates a CustomQuery Object. The constructor accepts a JSONspec, a
* DIBBs-defined JSON structure consisting of four keys corresponding to
* the four types of codes this data class encompasses. These Specs are
* currently located in the `customQueries` directory of the app.
* @param jsonSpec A JSON Object containing four code fields to load.
* @param patientId The ID of the patient to build into query strings.
*/
constructor(jsonSpec: any, patientId: string) {
try {
this.labCodes = jsonSpec?.labCodes || [];
this.snomedCodes = jsonSpec?.snomedCodes || [];
this.rxnormCodes = jsonSpec?.rxnormCodes || [];
this.classTypeCodes = jsonSpec?.classTypeCodes || [];
this.hasSecondEncounterQuery = jsonSpec?.hasSecondEncounterQuery || false;
this.compileQueries(patientId);
} catch (error) {
console.error("Could not create CustomQuery Object: ", error);
}
}

/**
* Compile the stored code information and given patientID into all applicable
* query strings for later use. If a particular code category has no values in
* the provided spec (e.g. a Newborn Screening case will have no rxnorm codes),
* any query built using those codes' filter will be left as the empty string.
* @param patientId The ID of the patient to query for.
*/
compileQueries(patientId: string): void {
const labsFilter = this.labCodes.join(",");
const snomedFilter = this.snomedCodes.join(",");
const rxnormFilter = this.rxnormCodes.join(",");
const classTypeFilter = this.classTypeCodes.join(",");

this.observationQuery =
labsFilter !== ""
? `/Observation?subject=Patient/${patientId}&code=${labsFilter}`
: "";
this.diagnosticReportQuery =
labsFilter !== ""
? `/DiagnosticReport?subject=${patientId}&code=${labsFilter}`
: "";
this.conditionQuery =
snomedFilter !== ""
? `/Condition?subject=${patientId}&code=${snomedFilter}`
: "";
this.medicationRequestQuery =
rxnormFilter !== ""
? `/MedicationRequest?subject=${patientId}&code=${rxnormFilter}&_include=MedicationRequest:medication&_include=MedicationRequest:medication.administration`
: "";
this.socialHistoryQuery = `/Observation?subject=${patientId}&category=social-history`;
this.encounterQuery =
snomedFilter !== ""
? `/Encounter?subject=${patientId}&reason-code=${snomedFilter}`
: "";
this.encounterClassTypeQuery =
classTypeFilter !== ""
? `/Encounter?subject=${patientId}&class=${classTypeFilter}`
: "";
}

/**
* Yields all non-empty-string queries compiled using the stored codes.
* @returns A list of queries.
*/
getAllQueries(): string[] {
const queryRequests: string[] = [
this.observationQuery,
this.diagnosticReportQuery,
this.conditionQuery,
this.medicationRequestQuery,
this.socialHistoryQuery,
this.encounterQuery,
this.encounterClassTypeQuery,
];
const filteredRequests = queryRequests.filter((q) => q !== "");
return filteredRequests;
}

/**
* Sometimes, only a single query is desired rather than a full list. In
* those cases, this function returns a single specified query string.
* @param desiredQuery The type of query the user wants.
* @returns The compiled query string for that type.
*/
getQuery(desiredQuery: string): string {
switch (desiredQuery) {
case "observation":
return this.observationQuery;
case "diagnostic":
return this.diagnosticReportQuery;
case "condition":
return this.conditionQuery;
case "medication":
return this.medicationRequestQuery;
case "social":
return this.socialHistoryQuery;
case "encounter":
return this.encounterQuery;
case "encounterClass":
return this.encounterClassTypeQuery;
default:
return "";
}
}
}
66 changes: 66 additions & 0 deletions containers/tefca-viewer/src/app/demoQueries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
export type QueryStruct = {
labCodes: string[];
snomedCodes: string[];
rxnormCodes: string[];
classTypeCodes: string[];
hasSecondEncounterQuery: boolean;
};

export const SOCIAL_DETERMINANTS_QUERY: QueryStruct = {
labCodes: [],
snomedCodes: [],
rxnormCodes: [],
classTypeCodes: [],
hasSecondEncounterQuery: false,
};

export const CANCER_QUERY: QueryStruct = {
labCodes: [],
snomedCodes: ["92814006"],
rxnormCodes: ["828265"],
classTypeCodes: [],
hasSecondEncounterQuery: true,
};

export const CHLAMYDIA_QUERY: QueryStruct = {
labCodes: ["24111-7", "72828-7", "21613-5", "82810-3", "11350-6", "83317-8"],
snomedCodes: ["2339001", "72531000052105", "102874004", "A74.9"],
rxnormCodes: ["434692", "82122", "1649987", "1665005"],
classTypeCodes: ["54", "441"],
hasSecondEncounterQuery: true,
};

export const GONORRHEA_QUERY: QueryStruct = {
labCodes: ["24111-7", "11350-6", "21613-5", "82810-3", "83317-8"],
snomedCodes: ["15628003", "2339001", "72531000052105", "102874004"],
rxnormCodes: ["1665005", "434692"],
classTypeCodes: ["54", "441"],
hasSecondEncounterQuery: true,
};

export const NEWBORN_SCREENING_QUERY: QueryStruct = {
labCodes: [
"73700-7",
"73698-3",
"54108-6",
"54109-4",
"58232-0",
"57700-7",
"73739-5",
"73742-9",
"2708-6",
"8336-0",
],
snomedCodes: [],
rxnormCodes: [],
classTypeCodes: [],
hasSecondEncounterQuery: false,
};

export const SYPHILIS_QUERY: QueryStruct = {
labCodes: ["LP70657-9", "53605-2"],
snomedCodes: ["76272004", "186847001"],
rxnormCodes: ["2671695"],
classTypeCodes: ["54", "441"],
hasSecondEncounterQuery: true,
};
Loading

0 comments on commit 7632e9f

Please sign in to comment.