Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix translation methods between query pages #214

Merged
merged 7 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions query-connector/src/app/api/query/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {

import { handleRequestError } from "./error-handling-service";
import { getSavedQueryByName } from "@/app/database-service";
import { mapQueryRowsToValueSets } from "@/app/utils";
import { unnestValueSetsFromQuery } from "@/app/utils";

/**
* Health check for TEFCA Viewer
Expand Down Expand Up @@ -90,7 +90,7 @@ export async function POST(request: NextRequest) {
// Lookup default parameters for particular use-case search
const queryName = UseCaseToQueryName[use_case as USE_CASES];
const queryResults = await getSavedQueryByName(queryName);
const valueSets = mapQueryRowsToValueSets(queryResults);
const valueSets = unnestValueSetsFromQuery(queryResults);

// Add params & patient identifiers to UseCaseRequest
const UseCaseRequest: UseCaseQueryRequest = {
Expand Down
3 changes: 1 addition & 2 deletions query-connector/src/app/database-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,7 @@ export async function getCustomQueries(): Promise<CustomUserQuery[]> {
q.query_data,
fzhao99 marked this conversation as resolved.
Show resolved Hide resolved
q.conditions_list
FROM
query q
WHERE q.query_name IN ('Gonorrhea case investigation', 'Newborn screening follow-up', 'Syphilis case investigation', 'Cancer case investigation', 'Chlamydia case investigation');
query q;
`;
// TODO: this will eventually need to take into account user permissions and specific authors
// We'll probably also need to refactor this to not show up in any user-facing containers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
hyperUnluckyPatient,
} from "@/app/constants";
import { getSavedQueryByName } from "@/app/database-service";
import { mapQueryRowsToValueSets } from "@/app/utils";
import { unnestValueSetsFromQuery } from "@/app/utils";
import { UseCaseQuery, UseCaseQueryResponse } from "@/app/query-service";
import { Patient } from "fhir/r4";

Expand All @@ -18,7 +18,7 @@ type SetStateCallback<T> = React.Dispatch<React.SetStateAction<T>>;
*/
export async function fetchUseCaseValueSets(queryName: string) {
const queryResults = await getSavedQueryByName(queryName);
const valueSets = mapQueryRowsToValueSets(queryResults);
const valueSets = unnestValueSetsFromQuery(queryResults);

return valueSets;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ import {
import {
CategoryNameToConditionOptionMap,
ConditionIdToValueSetArray,
mapFetchedDataToFrontendStructure,
groupConditionDataByCategoryName,
} from "../utils";
import { ConditionSelection } from "../components/ConditionSelection";
import { ValueSetSelection } from "../components/ValueSetSelection";
import SiteAlert from "@/app/query/designSystem/SiteAlert";
import { BuildStep } from "../../constants";
import LoadingView from "../../query/components/LoadingView";
import classNames from "classnames";
import { mapQueryRowsToValueSets } from "@/app/utils";
import { groupConditionConceptsIntoValueSets } from "@/app/utils";
import { batchToggleConcepts } from "../utils";

export type FormError = {
Expand Down Expand Up @@ -82,7 +82,8 @@ export default function QueryTemplateSelection() {
// if there are new ids, we need to query the db
if (idsToQuery && idsToQuery.length > 0) {
const results = await getValueSetsAndConceptsByConditionIDs(conditionIds);
const formattedResults = results && mapQueryRowsToValueSets(results);
const formattedResults =
results && groupConditionConceptsIntoValueSets(results);

// when fetching directly from conditions table (as opposed to a saved query),
// default to including all value sets
Expand Down Expand Up @@ -138,7 +139,7 @@ export default function QueryTemplateSelection() {

if (isSubscribed) {
setFetchedConditions(
mapFetchedDataToFrontendStructure(categoryToConditionArrayMap),
groupConditionDataByCategoryName(categoryToConditionArrayMap),
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import classNames from "classnames";
import { getConditionsData } from "@/app/database-service";
import {
CategoryNameToConditionOptionMap,
mapFetchedDataToFrontendStructure,
groupConditionDataByCategoryName,
ConditionIdToValueSetArray,
} from "../utils";
import ConditionColumnDisplay from "../buildFromTemplates/ConditionColumnDisplay";
Expand Down Expand Up @@ -78,7 +78,7 @@ export const ConditionSelection: React.FC<ConditionSelectionProps> = ({

if (isSubscribed) {
setFetchedConditions(
mapFetchedDataToFrontendStructure(categoryToConditionArrayMap),
groupConditionDataByCategoryName(categoryToConditionArrayMap),
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion query-connector/src/app/queryBuilding/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export type CategoryNameToConditionOptionMap = {
* category mapping
* @returns - The data in a CategoryNameToConditionOptionMap shape
*/
export function mapFetchedDataToFrontendStructure(fetchedData: {
export function groupConditionDataByCategoryName(fetchedData: {
[categoryName: string]: ConditionIdToNameMap[];
}) {
const result: CategoryNameToConditionOptionMap = {};
Expand Down
155 changes: 155 additions & 0 deletions query-connector/src/app/tests/unit/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import categoryToConditionArrayMap from "../assets/aphlCategoryMapping.json";
import queryTableDefaults from "../../assets/dibbs_db_seed_query.json";
import { ConditionIdToNameMap } from "@/app/queryBuilding/utils";
import { QueryResultRow } from "pg";

export const CATEGORY_TO_CONDITION_ARRAY_MAP =
categoryToConditionArrayMap as unknown as {
[categoryName: string]: ConditionIdToNameMap[];
};

export const DEFAULT_CHLAMYDIA_QUERY = [
queryTableDefaults.query.find((v) =>
v.query_name.includes("Chlamydia case investigation"),
),
] as QueryResultRow[];

export const EXPECTED_CHLAMYDIA_VALUESET_LENGTH = Object.values(
DEFAULT_CHLAMYDIA_QUERY[0].query_data[
"Chlamydia trachomatis infection (disorder)"
],
).length;

export const CANCER_VALUESETS = [
{
display: "RESULTS",
code_system: "http://cap.org/eCC",
code: "9484.100004300",
valueset_name: "Cancer (Leukemia) Lab Result",
valueset_id: "14_20240923",
valueset_external_id: "14",
version: "20240923",
author: "DIBBs",
type: "lrtc",
dibbs_concept_type: "labs",
condition_id: "2",
},
{
display:
"Presence of DNA mismatch repair protein MSH2 in primary malignant neoplasm of colon by immunohistochemistry (observable entity)",
code_system: "http://snomed.info/sct",
code: "1255068005",
valueset_name: "Cancer (Leukemia) Lab Result",
valueset_id: "14_20240923",
valueset_external_id: "14",
version: "20240923",
author: "DIBBs",
type: "lrtc",
dibbs_concept_type: "labs",
condition_id: "2",
},
{
display: "MSH2 Result",
code_system: "http://cap.org/eCC",
code: "30000.100004300",
valueset_name: "Cancer (Leukemia) Lab Result",
valueset_id: "14_20240923",
valueset_external_id: "14",
version: "20240923",
author: "DIBBs",
type: "lrtc",
dibbs_concept_type: "labs",
condition_id: "2",
},
{
display: "Malignant neoplastic disease (disorder)",
code_system: "http://snomed.info/sct",
code: "363346000",
valueset_name: "Suspected Cancer (Leukemia) Diagnosis",
valueset_id: "15_20240923",
valueset_external_id: "15",
version: "20240923",
author: "DIBBs",
type: "sdtc",
dibbs_concept_type: "conditions",
condition_id: "2",
},
{
display: "1 ML alemtuzumab 30 MG/ML Injection",
code_system: "http://www.nlm.nih.gov/research/umls/rxnorm",
code: "828265",
valueset_name: "Cancer (Leukemia) Medication",
valueset_id: "3_20240909",
valueset_external_id: "3",
version: "20240909",
author: "DIBBs",
type: "mrtc",
dibbs_concept_type: "medications",
condition_id: "2",
},
{
display: "Stage II (localized)",
code_system: "http://snomed.info/sct",
code: "36929009",
valueset_name: "Cancer (Leukemia) Diagnosis Problem",
valueset_id: "2_20240909",
valueset_external_id: "2",
version: "20240909",
author: "DIBBs",
type: "dxtc",
dibbs_concept_type: "conditions",
condition_id: "2",
},
{
display: "Stage group.clinical Cancer",
code_system: "http://loinc.org",
code: "21908-9",
valueset_name: "Cancer (Leukemia) Diagnosis Problem",
valueset_id: "2_20240909",
valueset_external_id: "2",
version: "20240909",
author: "DIBBs",
type: "dxtc",
dibbs_concept_type: "conditions",
condition_id: "2",
},
{
display: "Pathology Synoptic report",
code_system: "http://loinc.org",
code: "60568-3",
valueset_name: "Cancer (Leukemia) Diagnosis Problem",
valueset_id: "2_20240909",
valueset_external_id: "2",
version: "20240909",
author: "DIBBs",
type: "dxtc",
dibbs_concept_type: "conditions",
condition_id: "2",
},
{
display: "Chronic lymphoid leukemia, disease (disorder)",
code_system: "http://snomed.info/sct",
code: "92814006",
valueset_name: "Cancer (Leukemia) Diagnosis Problem",
valueset_id: "2_20240909",
valueset_external_id: "2",
version: "20240909",
author: "DIBBs",
type: "dxtc",
dibbs_concept_type: "conditions",
condition_id: "2",
},
{
display: "Allergy to sulfonamide",
code_system: "http://snomed.info/sct",
code: "418689008",
valueset_name: "Cancer (Leukemia) Diagnosis Problem",
valueset_id: "2_20240909",
valueset_external_id: "2",
version: "20240909",
author: "DIBBs",
type: "dxtc",
dibbs_concept_type: "conditions",
condition_id: "2",
},
];
64 changes: 50 additions & 14 deletions query-connector/src/app/tests/unit/utils.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import { DataDisplay, DataDisplayInfo } from "../../utils";
import categoryToConditionArrayMap from "../assets/aphlCategoryMapping.json";
import {
ConditionIdToNameMap,
mapFetchedDataToFrontendStructure,
DataDisplay,
DataDisplayInfo,
groupConditionConceptsIntoValueSets,
unnestValueSetsFromQuery,
} from "../../utils";

import {
groupConditionDataByCategoryName,
filterSearchByCategoryAndCondition,
} from "@/app/queryBuilding/utils";
import {
CANCER_VALUESETS,
CATEGORY_TO_CONDITION_ARRAY_MAP,
DEFAULT_CHLAMYDIA_QUERY,
EXPECTED_CHLAMYDIA_VALUESET_LENGTH,
} from "./fixtures";

describe("DataDisplay Component", () => {
it("should render the title and value", () => {
Expand Down Expand Up @@ -73,14 +83,12 @@ describe("DataDisplay Component", () => {
});
});

const TEST_FIXTURE = categoryToConditionArrayMap as unknown as {
[categoryName: string]: ConditionIdToNameMap[];
};

describe("data util methods for query building", () => {
describe("mapFetchedDataToFrontendStructure", () => {
describe("groupConditionDataByCategoryName", () => {
it("translates backend query to frontend dictionary structure", () => {
const mappedExample = mapFetchedDataToFrontendStructure(TEST_FIXTURE);
const mappedExample = groupConditionDataByCategoryName(
CATEGORY_TO_CONDITION_ARRAY_MAP,
);
// check a couple of random values to make sure mapping works correctly
expect(mappedExample["Injuries, NEC"][44301001].name).toBe(
"Suicide (event)",
Expand All @@ -97,8 +105,9 @@ describe("data util methods for query building", () => {

describe("filterSearchByCategoryAndCondition", () => {
it("filters by category (parent level)", () => {
const frontendStructuredData =
mapFetchedDataToFrontendStructure(TEST_FIXTURE);
const frontendStructuredData = groupConditionDataByCategoryName(
CATEGORY_TO_CONDITION_ARRAY_MAP,
);
const filterResults = filterSearchByCategoryAndCondition(
"Diseases",
frontendStructuredData,
Expand All @@ -107,8 +116,9 @@ describe("data util methods for query building", () => {
expect(Object.values(filterResults).length).toBe(9);
});
it("filters by condition (child level)", () => {
const frontendStructuredData =
mapFetchedDataToFrontendStructure(TEST_FIXTURE);
const frontendStructuredData = groupConditionDataByCategoryName(
CATEGORY_TO_CONDITION_ARRAY_MAP,
);
const filterResults = filterSearchByCategoryAndCondition(
"hepatitis",
frontendStructuredData,
Expand All @@ -123,4 +133,30 @@ describe("data util methods for query building", () => {
expect(countOfResults[0] + countOfResults[1]).toBe(8);
});
});

describe("unnestValueSetsFromQuery", () => {
const unnestedVals = unnestValueSetsFromQuery(DEFAULT_CHLAMYDIA_QUERY);
expect(unnestedVals.length).toBe(EXPECTED_CHLAMYDIA_VALUESET_LENGTH);
});

describe("groupConditionConceptsByValueSetId", () => {
const formattedValueSets =
groupConditionConceptsIntoValueSets(CANCER_VALUESETS);
const EXPECTED_CANCER_VALUESET_GROUPS = 4;
expect(formattedValueSets.length).toBe(EXPECTED_CANCER_VALUESET_GROUPS);
expect(
formattedValueSets.find((v) => v.valueSetId === "14_20240923")?.concepts
.length,
).toBe(3);
expect(
formattedValueSets.find((v) => v.valueSetId === "2_20240909")?.concepts
.length,
).toBe(5);

expect(
formattedValueSets
.find((v) => v.valueSetId === "2_20240909")
?.concepts.every((v) => Boolean(v)),
).toBeTrue();
});
});
Loading
Loading