From 7c56baf20d915893ee0af267d7021531a5fb88e1 Mon Sep 17 00:00:00 2001 From: fzhao99 Date: Thu, 31 Oct 2024 15:39:27 -0400 Subject: [PATCH] backend query insertion cleanup (#101) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- query-connector/src/app/constants.ts | 2 +- query-connector/src/app/database-service.ts | 6 +- query-connector/src/app/query-building.ts | 13 ++-- query-connector/src/app/query/SelectQuery.tsx | 2 +- .../app/query/components/CustomizeQuery.tsx | 69 +++++++++---------- .../src/app/query/components/utils.ts | 18 +++++ 6 files changed, 63 insertions(+), 47 deletions(-) create mode 100644 query-connector/src/app/query/components/utils.ts diff --git a/query-connector/src/app/constants.ts b/query-connector/src/app/constants.ts index 13cfc7216..bf529b3f6 100644 --- a/query-connector/src/app/constants.ts +++ b/query-connector/src/app/constants.ts @@ -331,7 +331,7 @@ export interface ValueSet { export const DEFAULT_ERSD_VERSION = "3"; -type DibbsConceptType = "labs" | "medications" | "conditions"; +export type DibbsConceptType = "labs" | "medications" | "conditions"; export type ErsdConceptType = | "ostc" | "lotc" diff --git a/query-connector/src/app/database-service.ts b/query-connector/src/app/database-service.ts index ecf27998a..5b2fb00b8 100644 --- a/query-connector/src/app/database-service.ts +++ b/query-connector/src/app/database-service.ts @@ -11,7 +11,7 @@ import { } from "./constants"; import { encode } from "base-64"; import { - UserQueryInput, + QueryInput, generateQueryInsertionSql, generateQueryToValueSetInsertionSql, } from "./query-building"; @@ -408,10 +408,10 @@ function logRejectedPromiseReasons( /** * Function that orchestrates query insertion for the query building flow - * @param input - Values of the shape UserQueryInput needed for query insertion + * @param input - Values of the shape QueryInput needed for query insertion * @returns - Success or failure status, with associated error message for frontend */ -export async function insertQuery(input: UserQueryInput) { +export async function insertQuery(input: QueryInput) { const { sql, values } = generateQueryInsertionSql(input); const insertUserQueryPromise = dbClient.query(sql, values); const errorArray = []; diff --git a/query-connector/src/app/query-building.ts b/query-connector/src/app/query-building.ts index de145902f..bce399f1d 100644 --- a/query-connector/src/app/query-building.ts +++ b/query-connector/src/app/query-building.ts @@ -1,13 +1,12 @@ import { UUID, randomUUID } from "crypto"; -import { Concept, ValueSet } from "./constants"; +import { ValueSet } from "./constants"; // TODO: Potentially merge this / infer this from the type created via the // database creation workstream -export type UserQueryInput = { +export type QueryInput = { queryName: string; author: string; valueSets: ValueSet[]; - concepts?: Concept[]; timeWindowUnit?: string; // TODO: should probably type this more strongly timeWindowNumber?: Number; }; @@ -19,10 +18,10 @@ const DEFAULT_TIME_WINDOW = { /** * Function that generates SQL needed for the query building flow - * @param input - Values of the shape UserQueryInput needed for query insertion + * @param input - Values of the shape QueryInput needed for query insertion * @returns [sql, values] needed for query building insertion */ -export function generateQueryInsertionSql(input: UserQueryInput) { +export function generateQueryInsertionSql(input: QueryInput) { const id = randomUUID(); const dateCreated = new Date().toISOString(); const dateLastModified = new Date().toISOString(); @@ -46,13 +45,13 @@ export function generateQueryInsertionSql(input: UserQueryInput) { /** * Function that generates SQL for the query_to_valueset join table needed for * query building. - * @param input - Values of the shape UserQueryInput needed for query insertion + * @param input - Values of the shape QueryInput needed for query insertion * @param queryId - ID of the query that's already been created to associate with * a given valueset * @returns An array of {sql, values} to be inserted by the join insertion flow */ export function generateQueryToValueSetInsertionSql( - input: UserQueryInput, + input: QueryInput, queryId: UUID, ) { const joinInsertionSqlArray = input.valueSets.map((v) => { diff --git a/query-connector/src/app/query/SelectQuery.tsx b/query-connector/src/app/query/SelectQuery.tsx index 7c1250240..e7af0c56a 100644 --- a/query-connector/src/app/query/SelectQuery.tsx +++ b/query-connector/src/app/query/SelectQuery.tsx @@ -120,7 +120,7 @@ const SelectQuery: React.FC = ({ setShowCustomizeQuery(false)} > diff --git a/query-connector/src/app/query/components/CustomizeQuery.tsx b/query-connector/src/app/query/components/CustomizeQuery.tsx index 4fc626de4..c4b51f068 100644 --- a/query-connector/src/app/query/components/CustomizeQuery.tsx +++ b/query-connector/src/app/query/components/CustomizeQuery.tsx @@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react"; import { Button } from "@trussworks/react-uswds"; import { + DibbsConceptType, DibbsValueSetType, USE_CASES, ValueSet, @@ -16,14 +17,18 @@ import CustomizeQueryAccordionHeader from "./customizeQuery/CustomizeQueryAccord import CustomizeQueryAccordionBody from "./customizeQuery/CustomizeQueryAccordionBody"; import Accordion from "../designSystem/Accordion"; import CustomizeQueryNav from "./customizeQuery/CustomizeQueryNav"; -import { mapValueSetsToValueSetTypes } from "./customizeQuery/customizeQueryUtils"; +import { + GroupedValueSet, + mapValueSetsToValueSetTypes, +} from "./customizeQuery/customizeQueryUtils"; import Backlink from "./backLink/Backlink"; import { RETURN_LABEL } from "../stepIndicator/StepIndicator"; +import { countDibbsConceptTypeToVsMapItems } from "./utils"; interface CustomizeQueryProps { useCaseQueryResponse: UseCaseQueryResponse; queryType: USE_CASES; - queryValuesets: ValueSet[]; + queryValueSets: ValueSet[]; setQueryValuesets: (queryVS: ValueSet[]) => void; goBack: () => void; } @@ -33,7 +38,7 @@ interface CustomizeQueryProps { * @param root0 - The properties object. * @param root0.useCaseQueryResponse - The response from the query service. * @param root0.queryType - The type of the query. - * @param root0.queryValuesets - The pre-fetched value sets from the DB. + * @param root0.queryValueSets - The pre-fetched value sets from the DB. * @param root0.setQueryValuesets - Function to update tracked custom query state. * @param root0.goBack - Back button to go from "customize-queries" to "search" component. * @returns The CustomizeQuery component. @@ -41,46 +46,40 @@ interface CustomizeQueryProps { const CustomizeQuery: React.FC = ({ useCaseQueryResponse, queryType, - queryValuesets, + queryValueSets: queryValueSets, setQueryValuesets, goBack, }) => { const [activeTab, setActiveTab] = useState("labs"); - const { labs, conditions, medications } = - mapValueSetsToValueSetTypes(queryValuesets); - const [valueSetOptions, setValueSetOptions] = useState({ - labs: labs, - conditions: conditions, - medications: medications, + + const [valueSetOptions, setValueSetOptions] = useState<{ + [dibbsConceptType in DibbsConceptType]: { + [vsNameAuthorSystem: string]: GroupedValueSet; + }; + }>({ + labs: {}, + conditions: {}, + medications: {}, }); + useEffect(() => { + const { labs, conditions, medications } = + mapValueSetsToValueSetTypes(queryValueSets); + + setValueSetOptions({ + labs: labs, + conditions: conditions, + medications: medications, + }); + }, [queryValueSets]); + // Compute counts of each tab-type - const countLabs = Object.values(valueSetOptions.labs).reduce( - (runningSum, gvs) => { - gvs.items.forEach((vs) => { - runningSum += vs.concepts.length; - }); - return runningSum; - }, - 0, - ); - const countConditions = Object.values(valueSetOptions.conditions).reduce( - (runningSum, gvs) => { - gvs.items.forEach((vs) => { - runningSum += vs.concepts.length; - }); - return runningSum; - }, - 0, + const countLabs = countDibbsConceptTypeToVsMapItems(valueSetOptions.labs); + const countConditions = countDibbsConceptTypeToVsMapItems( + valueSetOptions.conditions, ); - const countMedications = Object.values(valueSetOptions.medications).reduce( - (runningSum, gvs) => { - gvs.items.forEach((vs) => { - runningSum += vs.concepts.length; - }); - return runningSum; - }, - 0, + const countMedications = countDibbsConceptTypeToVsMapItems( + valueSetOptions.medications, ); // Keeps track of which side nav tab to display to users diff --git a/query-connector/src/app/query/components/utils.ts b/query-connector/src/app/query/components/utils.ts new file mode 100644 index 000000000..bb41bf37e --- /dev/null +++ b/query-connector/src/app/query/components/utils.ts @@ -0,0 +1,18 @@ +import { GroupedValueSet } from "./customizeQuery/customizeQueryUtils"; + +/** + * Utility function to count the number of labs / meds / conditions that we display + * on the customize query page + * @param obj a grouped ValueSet dictionary that we render as an individual accordion + * @returns A count of the number of items in each of the DibbsConceptTypes + */ +export const countDibbsConceptTypeToVsMapItems = (obj: { + [vsNameAuthorSystem: string]: GroupedValueSet; +}) => { + return Object.values(obj).reduce((runningSum, gvs) => { + gvs.items.forEach((vs) => { + runningSum += vs.concepts.length; + }); + return runningSum; + }, 0); +};