Skip to content

Commit

Permalink
Merge branch 'main' into shanice/deploy_demo_ecs
Browse files Browse the repository at this point in the history
  • Loading branch information
shanice-skylight authored Jan 10, 2025
2 parents 275215f + eaf17fd commit 24f2d6a
Show file tree
Hide file tree
Showing 23 changed files with 744 additions and 414 deletions.
6 changes: 3 additions & 3 deletions query-connector/e2e/alternate_queries.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ test.describe("alternate queries with the Query Connector", () => {
test("query using form-fillable demo patient by phone number", async ({
page,
}) => {
await page.getByRole("button", { name: "Go to the demo" }).click();
await page.getByRole("button", { name: "Try it out" }).click();
await page.getByRole("button", { name: "Fill fields" }).click();

// Delete Last name and MRN to force phone number as one of the 3 fields
Expand Down Expand Up @@ -50,7 +50,7 @@ test.describe("alternate queries with the Query Connector", () => {

// test("social determinants query with generalized function", async ({
test("cancer query with generalized function", async ({ page }) => {
await page.getByRole("button", { name: "Go to the demo" }).click();
await page.getByRole("button", { name: "Try it out" }).click();
await page.getByRole("button", { name: "Fill fields" }).click();
// Select FHIR server from drop down
await page.getByRole("button", { name: "Advanced" }).click();
Expand All @@ -77,7 +77,7 @@ test.describe("alternate queries with the Query Connector", () => {
test("form-fillable STI query using generalized function", async ({
page,
}) => {
await page.getByRole("button", { name: "Go to the demo" }).click();
await page.getByRole("button", { name: "Try it out" }).click();
await page.getByRole("button", { name: "Fill fields" }).click();
// Select FHIR server from drop down
await page.getByRole("button", { name: "Advanced" }).click();
Expand Down
2 changes: 1 addition & 1 deletion query-connector/e2e/customize_query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ test.describe("querying with the Query Connector", () => {
// Start every test by navigating to the customize query workflow
test.beforeEach(async ({ page }) => {
await page.goto(TEST_URL);
await page.getByRole("button", { name: "Go to the demo" }).click();
await page.getByRole("button", { name: "Try it out" }).click();

// Check that the info alert is visible and contains the correct text
const alert = page.locator(".custom-alert");
Expand Down
4 changes: 1 addition & 3 deletions query-connector/e2e/load.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,5 @@ test("landing page loads", async ({ page }) => {

// Check that interactable elements are present (header and Get Started)
await expect(page.getByRole("link", { name: metadata.title })).toBeVisible();
await expect(
page.getByRole("button", { name: "Go to the demo" }),
).toBeVisible();
await expect(page.getByRole("button", { name: "Try it out" })).toBeVisible();
});
4 changes: 2 additions & 2 deletions query-connector/e2e/query_workflow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test.describe("querying with the Query Connector", () => {
});

test("unsuccessful user query: no patients", async ({ page }) => {
await page.getByRole("button", { name: "Go to the demo" }).click();
await page.getByRole("button", { name: "Try it out" }).click();
await page.getByRole("button", { name: "Fill fields" }).click();
await page.getByLabel("First name").fill("Shouldnt");
await page.getByLabel("Last name").fill("Findanyone");
Expand All @@ -41,7 +41,7 @@ test.describe("querying with the Query Connector", () => {
});

test("successful demo user query", async ({ page }) => {
await page.getByRole("button", { name: "Go to the demo" }).click();
await page.getByRole("button", { name: "Try it out" }).click();

// Check that the info alert is visible and contains the correct text
const alert = page.locator(".custom-alert");
Expand Down
20 changes: 10 additions & 10 deletions query-connector/src/app/assets/dibbs_db_seed_query.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
"time_window_number": "30",
"time_window_unit": "days",
"query_name": "Cancer case investigation",
"conditions_list": "{Cancer (Leukemia)}",
"conditions_list": "{2}",
"query_data": {
"Cancer (Leukemia)": {
"2": {
"14_20240923": {
"valueSetId": "14_20240923",
"valueSetVersion": "20240923",
Expand Down Expand Up @@ -122,9 +122,9 @@
"time_window_number": "30",
"time_window_unit": "days",
"query_name": "Chlamydia case investigation",
"conditions_list": "{Chlamydia trachomatis infection (disorder)}",
"conditions_list": "{240589008}",
"query_data": {
"Chlamydia trachomatis infection (disorder)": {
"240589008": {
"2.16.840.1.113762.1.4.1146.999_20230602": {
"valueSetId": "2.16.840.1.113762.1.4.1146.999_20230602",
"valueSetVersion": "20230602",
Expand Down Expand Up @@ -2173,7 +2173,7 @@
"time_window_unit": "days",
"query_name": "Syphilis case investigation",
"query_data": {
"Congenital syphilis (disorder)": {
"35742006": {
"2.16.840.1.113762.1.4.1146.554_20191227": {
"valueSetId": "2.16.840.1.113762.1.4.1146.554_20191227",
"valueSetVersion": "20191227",
Expand Down Expand Up @@ -3347,7 +3347,7 @@
}
}
},
"conditions_list": "{Congenital syphilis (disorder)}"
"conditions_list": "{35742006}"
},
{
"author": "DIBBs",
Expand All @@ -3357,7 +3357,7 @@
"time_window_unit": "days",
"query_name": "Gonorrhea case investigation",
"query_data": {
"Gonorrhea (disorder)": {
"15628003": {
"2.16.840.1.113762.1.4.1146.1036_20190605": {
"valueSetId": "2.16.840.1.113762.1.4.1146.1036_20190605",
"valueSetVersion": "20190605",
Expand Down Expand Up @@ -4890,7 +4890,7 @@
}
}
},
"conditions_list": "{Gonorrhea (disorder)}"
"conditions_list": "{15628003}"
},
{
"author": "DIBBs",
Expand All @@ -4900,7 +4900,7 @@
"time_window_unit": "days",
"query_name": "Newborn screening follow-up",
"query_data": {
"Newborn Screening": {
"1": {
"1_20240909": {
"valueSetId": "1_20240909",
"valueSetVersion": "20240909",
Expand Down Expand Up @@ -5924,7 +5924,7 @@
}
}
},
"conditions_list": "{Newborn Screening}"
"conditions_list": "{1}"
}
]
}
89 changes: 88 additions & 1 deletion query-connector/src/app/backend/query-building.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"use server";

import { getDbClient } from "./dbClient";
import { QueryDetailsResult } from "../queryBuilding/utils";
import { NestedQuery, QueryDetailsResult } from "../queryBuilding/utils";
import { DibbsValueSet } from "../constants";
import { DEFAULT_TIME_WINDOW } from "../utils";
import { randomUUID } from "crypto";
const dbClient = getDbClient();

/**
Expand All @@ -28,3 +31,87 @@ export async function getSavedQueryDetails(queryId: string) {
console.error("Error retrieving query", error);
}
}

/**
* Backend handler function for upserting a query
* @param queryInput - frontend input for a query
* @param queryName - name of query
* @param author - author
* @param queryId - a queryId if previously defined
* @returns - all columns of the newly added row in the query table
*/
export async function saveCustomQuery(
queryInput: NestedQuery,
queryName: string,
author: string,
queryId?: string,
) {
const queryString = `
INSERT INTO query
values($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT(id)
DO UPDATE SET
query_name = EXCLUDED.query_name,
conditions_list = EXCLUDED.conditions_list,
query_data = EXCLUDED.query_data,
author = EXCLUDED.author,
date_last_modified = EXCLUDED.date_last_modified
RETURNING id, query_name;
`;
const { queryDataInsert, conditionInsert } =
formatQueryDataForDatabase(queryInput);

const NOW = new Date().toISOString();
try {
const dataToWrite = [
queryId ? queryId : randomUUID(),
queryName,
queryDataInsert,
conditionInsert,
author,
NOW,
NOW,
DEFAULT_TIME_WINDOW.timeWindowNumber,
DEFAULT_TIME_WINDOW.timeWindowUnit,
];
const result = await dbClient.query(queryString, dataToWrite);
if (result.rows.length > 0) {
return result.rows as unknown as QueryDetailsResult[];
}
console.error("Query save failed:", dataToWrite);
return [];
} catch (error) {
console.error("Error saving new query", error);
}
}

function formatQueryDataForDatabase(frontendInput: NestedQuery) {
const queryData: Record<string, { [valueSetId: string]: DibbsValueSet }> = {};
const conditionIds: string[] = [];

Object.entries(frontendInput).forEach(([conditionId, data]) => {
queryData[conditionId] = {};
conditionIds.push(conditionId);
Object.values(data).forEach((dibbsVsMap) => {
Object.entries(dibbsVsMap).forEach(([vsId, dibbsVs]) => {
queryData[conditionId][vsId] = dibbsVs;
});
});
});

return {
queryDataInsert: queryData,
conditionInsert: formatConditionsForPostgres(conditionIds),
};
}

function formatConditionsForPostgres(arr: string[]): string {
if (arr.length === 0) return "{}";

const escapedStrings = arr.map((str) => {
const escaped = str.replace(/"/g, '\\"');
return `"${escaped}"`;
});

return `{${escapedStrings.join(",")}}`;
}
16 changes: 8 additions & 8 deletions query-connector/src/app/page.module.scss
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
.pageSubtitle {
margin: 2rem 0 0.5rem 0;
margin: 2rem 0 0.5rem 0;
}

.pageSubtitle:last-of-type{
margin-top: 1.5rem;
.pageSubtitle:last-of-type {
margin-top: 1.5rem;
}

.pageContent {
font-weight: 300;
margin-top: 0;
font-size:1rem;
margin-bottom: 0.5rem
}
font-weight: 300;
margin-top: 0;
font-size: 1rem;
margin-bottom: 1rem;
}
27 changes: 9 additions & 18 deletions query-connector/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ export default function LandingPage() {
network of healthcare providers through your existing data use
agreements, giving you access to more complete and timely data
</h2>

<Button
className="next-button margin-bottom-2"
type="button"
id="next-button"
onClick={() => handleGoToDemo()}
>
Try it out
</Button>
</div>
<Image
alt="Graphic illustrating what TEFCA is"
Expand Down Expand Up @@ -81,24 +90,6 @@ export default function LandingPage() {
</ProcessListItem>
</ProcessList>
</div>
<div className="blue-background-container">
<div className="flex-justify-center flex-column">
<div className="text-holder">
<h2 className="font-sans-xs text-light margin-top-0">
Check out the Query Connector demo to try out features using
sample data.
</h2>
<Button
className="next-button"
type="button"
id="next-button"
onClick={() => handleGoToDemo()}
>
Go to the demo
</Button>
</div>
</div>
</div>
</div>
);
}
8 changes: 2 additions & 6 deletions query-connector/src/app/query-building.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { randomUUID } from "crypto";
import { DibbsValueSet } from "./constants";
import { DEFAULT_TIME_WINDOW } from "./utils";

// TODO: Potentially merge this / infer this from the type created via the
// database creation workstream
Expand All @@ -11,11 +12,6 @@ export type QueryInput = {
timeWindowNumber?: Number;
};

const DEFAULT_TIME_WINDOW = {
timeWindowNumber: 1,
timeWindowUnit: "day",
};

/**
* Function that generates SQL needed for the query building flow
* @param input - Values of the shape QueryInput needed for query insertion
Expand Down Expand Up @@ -46,6 +42,6 @@ export function generateQueryInsertionSql(input: QueryInput) {
export interface CustomUserQuery {
query_id: string;
query_name: string;
conditions_list?: string;
conditions_list?: string[];
valuesets: DibbsValueSet[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
.checkbox label::before {
width: 1.25rem;
height: 1.25rem;
min-width: 1.25rem;
box-shadow: 0 0 0 1px $black !important;
position: static;
margin: 0;
Expand Down
25 changes: 15 additions & 10 deletions query-connector/src/app/query/designSystem/drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type DrawerProps = {
isOpen: boolean;
onSave: () => void;
onClose: () => void;
onSearch?: () => void;
hasChanges: boolean;
};

Expand All @@ -20,6 +21,7 @@ type DrawerProps = {
* @param root0.title - The title displayed in the drawer.
* @param root0.placeholder - The placeholder text for the search field.
* @param root0.onClose - Function to handle closing the drawer.
* @param root0.onSearch - Function to handle search actions in the drawer.
* @param root0.isOpen - Boolean to control the visibility of the drawer.
* @param root0.toRender - The dynamic content to display.
* warning modal appears before saving
Expand All @@ -31,6 +33,7 @@ const Drawer: React.FC<DrawerProps> = ({
isOpen,
onClose,
toRender,
onSearch,
}: DrawerProps) => {
const handleClose = () => {
onClose();
Expand All @@ -52,16 +55,18 @@ const Drawer: React.FC<DrawerProps> = ({
</button>
<h2 className="margin-0 padding-bottom-2">{title}</h2>

<div className="padding-top-5">
<SearchField
id="searchFieldTemplate"
placeholder={placeholder}
className={styles.searchField}
onChange={(e) => {
e.preventDefault();
}}
/>
</div>
{onSearch && (
<div className="padding-top-5">
<SearchField
id="searchFieldTemplate"
placeholder={placeholder}
className={styles.searchField}
onChange={(e) => {
e.preventDefault();
}}
/>
</div>
)}
<div className="padding-top-2">{toRender}</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 24f2d6a

Please sign in to comment.