From 13c5ef7d8ad0f2321f6b3feb994361ff2140803f Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Wed, 11 Sep 2024 10:39:54 -0400 Subject: [PATCH 1/8] Add survey data on TestResultsListItem --- .../simplereport/db/model/auxiliary/TestResultsListItem.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/TestResultsListItem.java b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/TestResultsListItem.java index ac4a1c8b8c..823e47a2d9 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/TestResultsListItem.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/TestResultsListItem.java @@ -23,6 +23,7 @@ public class TestResultsListItem { private final TestCorrectionStatus correctionStatus; private final String reasonForCorrection; private final ApiUser createdBy; + private final AskOnEntrySurvey surveyData; public TestResultsListItem(Result result) { this.id = result.getTestEvent().getInternalId(); @@ -37,5 +38,6 @@ public TestResultsListItem(Result result) { this.correctionStatus = result.getTestEvent().getCorrectionStatus(); this.reasonForCorrection = result.getTestEvent().getReasonForCorrection(); this.createdBy = result.getTestEvent().getCreatedBy(); + this.surveyData = result.getTestEvent().getSurveyData(); } } From c196690052178668969eb573487980a059298a97 Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Wed, 11 Sep 2024 14:52:42 -0400 Subject: [PATCH 2/8] Use GetResultsForDownload query for csv download --- .../src/main/resources/graphql/main.graphqls | 10 + .../src/app/testResults/operations.graphql | 91 ++++++ .../DownloadResultsCsvModal.test.tsx | 80 +++--- .../viewResults/DownloadResultsCsvModal.tsx | 21 +- .../viewResults/TestResultsList.test.tsx | 30 +- frontend/src/app/utils/testResultCSV.test.ts | 203 +++++++------ frontend/src/app/utils/testResultCSV.ts | 55 +--- frontend/src/generated/graphql.tsx | 271 ++++++++++++++++++ 8 files changed, 565 insertions(+), 196 deletions(-) diff --git a/backend/src/main/resources/graphql/main.graphqls b/backend/src/main/resources/graphql/main.graphqls index 9d255a5d60..127b088292 100644 --- a/backend/src/main/resources/graphql/main.graphqls +++ b/backend/src/main/resources/graphql/main.graphqls @@ -308,6 +308,15 @@ type TestResultsPage { content: [TestResult] } +type AskOnEntrySurvey { + pregnancy: String + syphilisHistory: String + symptoms: String + symptomOnset: LocalDate + noSymptoms: Boolean + genderOfSexualPartners: [String] +} + type Result { id: ID! facility: Facility! @@ -321,6 +330,7 @@ type Result { correctionStatus: String reasonForCorrection: String createdBy: ApiUser + surveyData: AskOnEntrySurvey } type ResultsPage { diff --git a/frontend/src/app/testResults/operations.graphql b/frontend/src/app/testResults/operations.graphql index 0a98d84c92..72a73b6484 100644 --- a/frontend/src/app/testResults/operations.graphql +++ b/frontend/src/app/testResults/operations.graphql @@ -164,6 +164,97 @@ query GetResultsMultiplexWithCount( } } +query GetResultsForDownload( + $facilityId: ID + $patientId: ID + $result: String + $role: String + $disease: String + $startDate: DateTime + $endDate: DateTime + $pageNumber: Int + $pageSize: Int +) { + resultsPage( + facilityId: $facilityId + patientId: $patientId + result: $result + role: $role + disease: $disease + startDate: $startDate + endDate: $endDate + pageNumber: $pageNumber + pageSize: $pageSize + ) { + content { + id + dateAdded + dateUpdated + dateTested + disease + testResult + correctionStatus + reasonForCorrection + facility { + name + isDeleted + } + deviceType { + name + manufacturer + model + swabTypes { + internalId, + name + } + } + patient { + firstName + middleName + lastName + birthDate + gender + race + ethnicity + tribalAffiliation + role + lookupId + street + streetTwo + city + county + state + zipCode + country + email + phoneNumbers { + type + number + } + residentCongregateSetting + employedInHealthcare + preferredLanguage + } + createdBy { + nameInfo { + firstName + middleName + lastName + } + } + surveyData { + pregnancy + syphilisHistory + symptoms + symptomOnset + noSymptoms + genderOfSexualPartners + } + } + totalElements + } +} + query GetAllFacilities($showArchived: Boolean) { facilities(showArchived: $showArchived) { id diff --git a/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.test.tsx b/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.test.tsx index 5caa5bfa5b..b6788eba92 100644 --- a/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.test.tsx +++ b/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.test.tsx @@ -3,10 +3,52 @@ import { MockedProvider } from "@apollo/client/testing"; import ReactDOM from "react-dom"; import userEvent from "@testing-library/user-event"; -import { GetFacilityResultsForCsvWithCountDocument } from "../../../generated/graphql"; +import { GetResultsForDownloadDocument } from "../../../generated/graphql"; +import { QueriedTestResult } from "../../utils/testResultCSV"; import { DownloadResultsCsvModal } from "./DownloadResultsCsvModal"; +const mockedQueriedTestResults: QueriedTestResult[] = [ + { + id: "1259eb00-1ec6-4611-83ee-d6a988687c5f", + dateAdded: "2023-02-28T14:35:13.975Z", + patient: {}, + createdBy: { + nameInfo: { + firstName: "", + middleName: "", + lastName: "", + }, + }, + facility: { + name: "1677594642-Russel - Mann", + isDeleted: null, + __typename: "Facility", + }, + dateTested: "2023-02-28T14:35:13.975Z", + dateUpdated: "2023-02-28T14:35:13.975Z", + testResult: "UNDETERMINED", + disease: "COVID-19", + correctionStatus: "ORIGINAL", + reasonForCorrection: null, + surveyData: { + noSymptoms: false, + symptoms: '{"64531003":"false"}', + }, + deviceType: { + name: "Abbott IDNow", + manufacturer: "Abbott", + model: "ID Now", + swabTypes: [ + { + internalId: "445297001", + name: "445297001", + }, + ], + }, + }, +]; + const mockFacilityID = "b0d2041f-93c9-4192-b19a-dd99c0044a7e"; const graphqlMocks = [ { @@ -16,43 +58,13 @@ const graphqlMocks = [ pageNumber: 0, pageSize: 1, }, - query: GetFacilityResultsForCsvWithCountDocument, + query: GetResultsForDownloadDocument, fetchPolicy: "no-cache", }, result: { data: { - testResultsPage: { - content: [ - { - patient: {}, - createdBy: { nameInfo: {} }, - noSymptoms: false, - symptoms: '{"64531003":"false"}', - facility: { - name: "1677594642-Russel - Mann", - isDeleted: null, - __typename: "Facility", - }, - dateTested: "2023-02-28T14:35:13.975Z", - dateUpdated: "2023-02-28T14:35:13.975Z", - results: [ - { - disease: { name: "COVID-19", __typename: "SupportedDisease" }, - testResult: "UNDETERMINED", - __typename: "MultiplexResult", - }, - ], - correctionStatus: "ORIGINAL", - reasonForCorrection: null, - deviceType: { - name: "Abbott IDNow", - manufacturer: "Abbott", - model: "ID Now", - swabType: "445297001", - __typename: "DeviceType", - }, - }, - ], + resultsPage: { + content: mockedQueriedTestResults, totalElements: 1, }, }, diff --git a/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.tsx b/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.tsx index 1e4cfdf75f..409905e885 100644 --- a/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.tsx +++ b/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.tsx @@ -8,11 +8,10 @@ import { ApolloError } from "@apollo/client"; import { showError } from "../../utils/srToast"; import Button from "../../commonComponents/Button/Button"; import { - useGetFacilityResultsForCsvWithCountLazyQuery, - GetFacilityResultsForCsvWithCountQuery, + GetResultsForDownloadQuery, + useGetResultsForDownloadLazyQuery, } from "../../../generated/graphql"; import { parseDataForCSV, ResultCsvRow } from "../../utils/testResultCSV"; -import { useDisabledFeatureDiseaseList } from "../../utils/disease"; import { ALL_FACILITIES_ID, ResultsQueryVariables } from "./TestResultsList"; @@ -38,7 +37,6 @@ export const DownloadResultsCsvModal = ({ >(null); // Disable downloads because backend will hang on over 20k results (#3953) const disableDownload = totalEntries > rowsMaxLimit; - const disabledFeatureDiseaseList = useDisabledFeatureDiseaseList(); const filtersPresent = Object.entries(filterParams).some(([key, val]) => { // active facility in the facility filter is the default @@ -59,22 +57,17 @@ export const DownloadResultsCsvModal = ({ }; const [downloadTestResultsQuery, { loading }] = - useGetFacilityResultsForCsvWithCountLazyQuery({ + useGetResultsForDownloadLazyQuery({ variables, fetchPolicy: "no-cache", - onCompleted: (data: GetFacilityResultsForCsvWithCountQuery) => - handleComplete(data), + onCompleted: (data: GetResultsForDownloadQuery) => handleComplete(data), onError: (error: ApolloError) => handleError(error), }); - const handleComplete = (data: GetFacilityResultsForCsvWithCountQuery) => { - if (data?.testResultsPage?.content) { + const handleComplete = (data: GetResultsForDownloadQuery) => { + if (data?.resultsPage?.content) { try { - const csvResults = parseDataForCSV( - data.testResultsPage.content, - disabledFeatureDiseaseList, - filterParams - ); + const csvResults = parseDataForCSV(data.resultsPage.content); setResults(csvResults); } catch (e) { showError("Error creating results file to download"); diff --git a/frontend/src/app/testResults/viewResults/TestResultsList.test.tsx b/frontend/src/app/testResults/viewResults/TestResultsList.test.tsx index 481618d0e1..719fd23fbc 100644 --- a/frontend/src/app/testResults/viewResults/TestResultsList.test.tsx +++ b/frontend/src/app/testResults/viewResults/TestResultsList.test.tsx @@ -11,8 +11,12 @@ import { mocksWithMultiplex, mockWithFacilityAndPositiveResult, } from "../mocks/queries.mock"; -import { GetAllFacilitiesDocument } from "../../../generated/graphql"; +import { + GetAllFacilitiesDocument, + GetResultsForDownloadDocument, +} from "../../../generated/graphql"; import { facilities } from "../mocks/facilities.mock"; +import testResultsMultiplex from "../mocks/resultsMultiplex.mock"; import TestResultsList, { ALL_FACILITIES_ID } from "./TestResultsList"; import * as UtilsMock from "./utils"; @@ -236,12 +240,12 @@ describe("TestResultsList", () => { }); describe("with mocks", () => { - const renderWithUser = () => ({ + const renderWithUser = (localMocks?: any[]) => ({ user: userEvent.setup(), ...render( - + @@ -549,7 +553,25 @@ describe("TestResultsList", () => { }); it("closes the download test results modal after downloading", async () => { - const { user } = renderWithUser(); + const localMocks = mocks.slice(1, 3).concat([ + { + request: { + query: GetResultsForDownloadDocument, + variables: { + facilityId: "1", + pageNumber: 0, + pageSize: 3, + }, + }, + result: { + data: { + resultsPage: testResultsMultiplex, + }, + }, + }, + ]); + + const { user } = renderWithUser(localMocks); // source of "navigation not implemented" error expect(await screen.findByText("Showing results for 1-3 of 3 tests")); expect( diff --git a/frontend/src/app/utils/testResultCSV.test.ts b/frontend/src/app/utils/testResultCSV.test.ts index 654dcb9936..0882bdc0b2 100644 --- a/frontend/src/app/utils/testResultCSV.test.ts +++ b/frontend/src/app/utils/testResultCSV.test.ts @@ -1,101 +1,106 @@ -import { MULTIPLEX_DISEASES } from "../testResults/constants"; - import { parseDataForCSV, QueriedTestResult } from "./testResultCSV"; -const data = [ - { - facility: { - name: "test facility", - isDeleted: false, - }, - dateTested: "2022-06-13T19:24:31.187Z", - dateUpdated: "2022-03-13T19:24:31.187Z", - results: [ - { - disease: { - name: "COVID-19", - }, - testResult: "NEGATIVE", - }, - { - disease: { - name: "Flu A", - }, - testResult: "NEGATIVE", - }, - { - disease: { - name: "Flu B", - }, - testResult: "NEGATIVE", - }, - { - disease: { - name: "RSV", - }, - testResult: "UNDETERMINED", - }, +const genericTestResult: Partial = { + facility: { + name: "test facility", + isDeleted: false, + }, + dateTested: "2022-06-13T19:24:31.187Z", + dateUpdated: "2022-03-13T19:24:31.187Z", + dateAdded: "2022-03-13T19:24:31.187Z", + correctionStatus: "REMOVED", + reasonForCorrection: "DUPLICATE_TEST", + deviceType: { + name: "Access Bio CareStart", + manufacturer: "Access Bio, Inc.", + model: "CareStart COVID-19 Antigen test*", + swabTypes: [ { - disease: { - name: "HIV", - }, - testResult: "POSITIVE", + internalId: "2f5188b9-1e91-49cc-b004-340689e846e9", + name: "Nasal swab", }, + ], + }, + patient: { + firstName: "John", + middleName: "E", + lastName: "Doe", + birthDate: "1980-01-01", + gender: "female", + race: "white", + ethnicity: "hispanic", + tribalAffiliation: [null], + lookupId: null, + phoneNumbers: [ { - disease: { - name: "Syphilis", - }, - testResult: "POSITIVE", + number: "(123) 456-7890", }, ], - correctionStatus: "REMOVED", - reasonForCorrection: "DUPLICATE_TEST", - deviceType: { - name: "Access Bio CareStart", - manufacturer: "Access Bio, Inc.", - model: "CareStart COVID-19 Antigen test*", - swabTypes: [ - { - name: "Nasal swab", - }, - ], - }, - patient: { - firstName: "John", - middleName: "E", + email: "foo@bar.com", + street: "1234 Green Street", + streetTwo: "", + city: "Minneapolis", + county: null, + state: "MN", + zipCode: "90210", + country: "USA", + role: "STAFF", + residentCongregateSetting: null, + employedInHealthcare: null, + preferredLanguage: "English", + }, + createdBy: { + nameInfo: { + firstName: "Jane", + middleName: null, lastName: "Doe", - birthDate: "1980-01-01", - gender: "female", - race: "white", - ethnicity: "hispanic", - tribalAffiliation: [null], - lookupId: null, - telephone: "(123) 456-7890", - email: "foo@bar.com", - street: "1234 Green Street", - streetTwo: "", - city: "Minneapolis", - county: null, - state: "MN", - zipCode: "90210", - country: "USA", - role: "STAFF", - residentCongregateSetting: null, - employedInHealthcare: null, - preferredLanguage: "English", - }, - createdBy: { - nameInfo: { - firstName: "Jane", - middleName: null, - lastName: "Doe", - }, }, + }, + surveyData: { symptoms: '{"64531003":"false","103001002":"false","84229001":"false","68235000":"false","426000000":"false","49727002":"false","68962001":"false","422587007":"false","267036007":"false","62315008":"false","43724002":"false","36955009":"false","44169009":"false","422400008":"false","230145002":"false","25064002":"false","162397003":"false"}', noSymptoms: false, symptomOnset: null, }, +}; + +const data: QueriedTestResult[] = [ + { + ...genericTestResult, + id: "551150c4-8e48-4cb6-a2fa-74c52b8ca96f", + disease: "COVID-19", + testResult: "NEGATIVE", + }, + { + ...genericTestResult, + id: "53d59cbf-133d-46be-9bc2-751a9e935a06", + disease: "Flu A", + testResult: "NEGATIVE", + }, + { + ...genericTestResult, + id: "6a92fef7-97f3-4929-9904-4d22d781e7f6", + disease: "Flu B", + testResult: "NEGATIVE", + }, + { + ...genericTestResult, + id: "72b26d81-860c-4394-8de5-f30aa91b79d7", + disease: "RSV", + testResult: "UNDETERMINED", + }, + { + ...genericTestResult, + id: "398e85b1-9355-41cd-a1ca-ce95f7dc2a01", + disease: "HIV", + testResult: "POSITIVE", + }, + { + ...genericTestResult, + id: "d4b23feb-96e3-4a88-bb63-699a865aaa3c", + disease: "Syphilis", + testResult: "POSITIVE", + }, ] as QueriedTestResult[]; const csvRowWithoutResult = { @@ -137,11 +142,15 @@ const csvRowWithoutResult = { "Test date": "06/13/2022 7:24pm", }; -const resultNoSTD = [ +const covidResultRow = { + ...csvRowWithoutResult, + Condition: "COVID-19", + Result: "NEGATIVE", +}; + +const csvResultRows = [ { - ...csvRowWithoutResult, - Condition: "COVID-19", - Result: "NEGATIVE", + ...covidResultRow, }, { ...csvRowWithoutResult, @@ -158,10 +167,6 @@ const resultNoSTD = [ Condition: "RSV", Result: "UNDETERMINED", }, -]; - -const resultAllDiseases = [ - ...resultNoSTD, { ...csvRowWithoutResult, Condition: "HIV", @@ -176,24 +181,16 @@ const resultAllDiseases = [ describe("parseDataForCSV", () => { it("parses multiplex data", () => { - expect(parseDataForCSV(data)).toEqual(resultAllDiseases); + expect(parseDataForCSV(data)).toEqual(csvResultRows); }); it("parse data does not fail if tribalAffiliation is null", () => { expect( parseDataForCSV([ { - ...data[0], + ...data[0]!, patient: { ...data[0]?.patient, tribalAffiliation: null }, }, ]) - ).toEqual(resultAllDiseases); - }); - it("doesn't include STD data if feature flags are false", () => { - expect( - parseDataForCSV(data, [ - MULTIPLEX_DISEASES.HIV, - MULTIPLEX_DISEASES.SYPHILIS, - ]) - ).toEqual(resultNoSTD); + ).toEqual([covidResultRow]); }); }); diff --git a/frontend/src/app/utils/testResultCSV.ts b/frontend/src/app/utils/testResultCSV.ts index dcbfab4c74..819d48566e 100644 --- a/frontend/src/app/utils/testResultCSV.ts +++ b/frontend/src/app/utils/testResultCSV.ts @@ -1,17 +1,14 @@ import moment from "moment"; import { byDateTested } from "../testResults/viewResults/TestResultsList"; -import { MULTIPLEX_DISEASES } from "../testResults/constants"; -import { GetFacilityResultsForCsvWithCountQuery } from "../../generated/graphql"; +import { GetResultsForDownloadQuery } from "../../generated/graphql"; import { hasSymptomsForView, symptomsStringToArray } from "./symptoms"; import { displayFullName, facilityDisplayName } from "./index"; export type QueriedTestResult = NonNullable< - NonNullable< - GetFacilityResultsForCsvWithCountQuery["testResultsPage"] - >["content"] + NonNullable["content"] >[number]; const resultCSVHeadersString = [ @@ -74,18 +71,16 @@ export type ResultCsvRow = | undefined; }; -export function parseDataForCSV( - data: QueriedTestResult[], - excludedDiseases: MULTIPLEX_DISEASES[] = [], - filterParams?: FilterParams -): ResultCsvRow[] { +export function parseDataForCSV(data: QueriedTestResult[]): ResultCsvRow[] { let csvRows: ResultCsvRow[] = []; data.sort(byDateTested).forEach((r: QueriedTestResult) => { - const symptomList = r?.symptoms ? symptomsStringToArray(r.symptoms) : []; + const symptomList = r?.surveyData?.symptoms + ? symptomsStringToArray(r.surveyData.symptoms) + : []; const swabTypes = r?.deviceType?.swabTypes ?? []; - const csvOrderedColumns1: Partial = { + const resultRow: ResultCsvRow = { "Patient first name": r?.patient?.firstName, "Patient middle name": r?.patient?.middleName, "Patient last name": r?.patient?.lastName, @@ -98,8 +93,8 @@ export function parseDataForCSV( "MM/DD/YYYY" ), "Test date": moment(r?.dateTested).format("MM/DD/YYYY h:mma"), - }; - const csvOrderedColumns2: Partial = { + Condition: r?.disease, + Result: r?.testResult, "Result reported date": moment(r?.dateUpdated).format("MM/DD/YYYY h:mma"), "Test correction status": r?.correctionStatus, "Test correction reason": r?.reasonForCorrection, @@ -108,12 +103,12 @@ export function parseDataForCSV( "Device model": r?.deviceType?.model, "Device swab type": swabTypes.length > 0 ? swabTypes[0].name : "", "Has symptoms": hasSymptomsForView( - r?.noSymptoms ?? false, - r?.symptoms ?? "{}" + r?.surveyData?.noSymptoms ?? false, + r?.surveyData?.symptoms ?? "{}" ), "Symptoms present": symptomList.length > 0 ? symptomList.join(", ") : "No symptoms", - "Symptom onset": moment(r?.symptomOnset).format("MM/DD/YYYY"), + "Symptom onset": moment(r?.surveyData?.symptomOnset).format("MM/DD/YYYY"), "Facility name": facilityDisplayName( r?.facility?.name ?? "", r?.facility?.isDeleted ?? false @@ -126,7 +121,7 @@ export function parseDataForCSV( "Patient role": r?.patient?.role, "Patient ID (Student ID, Employee ID, etc.)": r?.patient?.lookupId, "Patient preferred language": r?.patient?.preferredLanguage, - "Patient phone number": r?.patient?.telephone, + "Patient phone number": r?.patient?.phoneNumbers?.at(0)?.number ?? "", "Patient email": r?.patient?.email, "Patient street address": r?.patient?.street, "Patient street address 2": r?.patient?.streetTwo, @@ -144,29 +139,7 @@ export function parseDataForCSV( r?.patient?.residentCongregateSetting, "Patient is employed in healthcare": r?.patient?.employedInHealthcare, }; - - // individual rows for each result on a single test event - r?.results?.forEach((result) => { - if ( - excludedDiseases.some( - (d) => d.toLowerCase() === result?.disease.name.toLowerCase() - ) || - (filterParams?.disease && - result?.disease.name.toLowerCase() !== - filterParams.disease.toLowerCase()) - ) { - return; - } - const csvConditionData = { - Condition: result?.disease.name, - Result: result?.testResult ?? "Unknown", - }; - csvRows.push({ - ...csvOrderedColumns1, - ...csvConditionData, - ...csvOrderedColumns2, - } as ResultCsvRow); - }); + csvRows.push(resultRow); }); return csvRows; } diff --git a/frontend/src/generated/graphql.tsx b/frontend/src/generated/graphql.tsx index 2c271b39a8..bbb2a32f55 100644 --- a/frontend/src/generated/graphql.tsx +++ b/frontend/src/generated/graphql.tsx @@ -99,6 +99,16 @@ export enum ArchivedStatus { Unarchived = "UNARCHIVED", } +export type AskOnEntrySurvey = { + __typename?: "AskOnEntrySurvey"; + genderOfSexualPartners?: Maybe>>; + noSymptoms?: Maybe; + pregnancy?: Maybe; + symptomOnset?: Maybe; + symptoms?: Maybe; + syphilisHistory?: Maybe; +}; + export type CreateDeviceType = { manufacturer: Scalars["String"]["input"]; model: Scalars["String"]["input"]; @@ -922,6 +932,7 @@ export type Result = { id: Scalars["ID"]["output"]; patient: Patient; reasonForCorrection?: Maybe; + surveyData?: Maybe; testResult: Scalars["String"]["output"]; }; @@ -2547,6 +2558,100 @@ export type GetResultsMultiplexWithCountQuery = { } | null; }; +export type GetResultsForDownloadQueryVariables = Exact<{ + facilityId?: InputMaybe; + patientId?: InputMaybe; + result?: InputMaybe; + role?: InputMaybe; + disease?: InputMaybe; + startDate?: InputMaybe; + endDate?: InputMaybe; + pageNumber?: InputMaybe; + pageSize?: InputMaybe; +}>; + +export type GetResultsForDownloadQuery = { + __typename?: "Query"; + resultsPage?: { + __typename?: "ResultsPage"; + totalElements?: number | null; + content?: Array<{ + __typename?: "Result"; + id: string; + dateAdded: string; + dateUpdated?: any | null; + dateTested: any; + disease: string; + testResult: string; + correctionStatus?: string | null; + reasonForCorrection?: string | null; + facility: { + __typename?: "Facility"; + name: string; + isDeleted?: boolean | null; + }; + deviceType: { + __typename?: "DeviceType"; + name: string; + manufacturer: string; + model: string; + swabTypes: Array<{ + __typename?: "SpecimenType"; + internalId: string; + name: string; + }>; + }; + patient: { + __typename?: "Patient"; + firstName?: string | null; + middleName?: string | null; + lastName?: string | null; + birthDate?: any | null; + gender?: string | null; + race?: string | null; + ethnicity?: string | null; + tribalAffiliation?: Array | null; + role?: string | null; + lookupId?: string | null; + street?: string | null; + streetTwo?: string | null; + city?: string | null; + county?: string | null; + state?: string | null; + zipCode?: string | null; + country?: string | null; + email?: string | null; + residentCongregateSetting?: boolean | null; + employedInHealthcare?: boolean | null; + preferredLanguage?: string | null; + phoneNumbers?: Array<{ + __typename?: "PhoneNumber"; + type?: PhoneType | null; + number?: string | null; + } | null> | null; + }; + createdBy?: { + __typename?: "ApiUser"; + nameInfo?: { + __typename?: "NameInfo"; + firstName?: string | null; + middleName?: string | null; + lastName: string; + } | null; + } | null; + surveyData?: { + __typename?: "AskOnEntrySurvey"; + pregnancy?: string | null; + syphilisHistory?: string | null; + symptoms?: string | null; + symptomOnset?: any | null; + noSymptoms?: boolean | null; + genderOfSexualPartners?: Array | null; + } | null; + } | null> | null; + } | null; +}; + export type GetAllFacilitiesQueryVariables = Exact<{ showArchived?: InputMaybe; }>; @@ -8073,6 +8178,172 @@ export type GetResultsMultiplexWithCountQueryResult = Apollo.QueryResult< GetResultsMultiplexWithCountQuery, GetResultsMultiplexWithCountQueryVariables >; +export const GetResultsForDownloadDocument = gql` + query GetResultsForDownload( + $facilityId: ID + $patientId: ID + $result: String + $role: String + $disease: String + $startDate: DateTime + $endDate: DateTime + $pageNumber: Int + $pageSize: Int + ) { + resultsPage( + facilityId: $facilityId + patientId: $patientId + result: $result + role: $role + disease: $disease + startDate: $startDate + endDate: $endDate + pageNumber: $pageNumber + pageSize: $pageSize + ) { + content { + id + dateAdded + dateUpdated + dateTested + disease + testResult + correctionStatus + reasonForCorrection + facility { + name + isDeleted + } + deviceType { + name + manufacturer + model + swabTypes { + internalId + name + } + } + patient { + firstName + middleName + lastName + birthDate + gender + race + ethnicity + tribalAffiliation + role + lookupId + street + streetTwo + city + county + state + zipCode + country + email + phoneNumbers { + type + number + } + residentCongregateSetting + employedInHealthcare + preferredLanguage + } + createdBy { + nameInfo { + firstName + middleName + lastName + } + } + surveyData { + pregnancy + syphilisHistory + symptoms + symptomOnset + noSymptoms + genderOfSexualPartners + } + } + totalElements + } + } +`; + +/** + * __useGetResultsForDownloadQuery__ + * + * To run a query within a React component, call `useGetResultsForDownloadQuery` and pass it any options that fit your needs. + * When your component renders, `useGetResultsForDownloadQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetResultsForDownloadQuery({ + * variables: { + * facilityId: // value for 'facilityId' + * patientId: // value for 'patientId' + * result: // value for 'result' + * role: // value for 'role' + * disease: // value for 'disease' + * startDate: // value for 'startDate' + * endDate: // value for 'endDate' + * pageNumber: // value for 'pageNumber' + * pageSize: // value for 'pageSize' + * }, + * }); + */ +export function useGetResultsForDownloadQuery( + baseOptions?: Apollo.QueryHookOptions< + GetResultsForDownloadQuery, + GetResultsForDownloadQueryVariables + > +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery< + GetResultsForDownloadQuery, + GetResultsForDownloadQueryVariables + >(GetResultsForDownloadDocument, options); +} +export function useGetResultsForDownloadLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + GetResultsForDownloadQuery, + GetResultsForDownloadQueryVariables + > +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery< + GetResultsForDownloadQuery, + GetResultsForDownloadQueryVariables + >(GetResultsForDownloadDocument, options); +} +export function useGetResultsForDownloadSuspenseQuery( + baseOptions?: Apollo.SuspenseQueryHookOptions< + GetResultsForDownloadQuery, + GetResultsForDownloadQueryVariables + > +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useSuspenseQuery< + GetResultsForDownloadQuery, + GetResultsForDownloadQueryVariables + >(GetResultsForDownloadDocument, options); +} +export type GetResultsForDownloadQueryHookResult = ReturnType< + typeof useGetResultsForDownloadQuery +>; +export type GetResultsForDownloadLazyQueryHookResult = ReturnType< + typeof useGetResultsForDownloadLazyQuery +>; +export type GetResultsForDownloadSuspenseQueryHookResult = ReturnType< + typeof useGetResultsForDownloadSuspenseQuery +>; +export type GetResultsForDownloadQueryResult = Apollo.QueryResult< + GetResultsForDownloadQuery, + GetResultsForDownloadQueryVariables +>; export const GetAllFacilitiesDocument = gql` query GetAllFacilities($showArchived: Boolean) { facilities(showArchived: $showArchived) { From 920b24f76edf29d72520aecbc8e0723258b19a0f Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Thu, 12 Sep 2024 15:34:15 -0400 Subject: [PATCH 3/8] Add resolved survey data --- .../api/testresult/ResultDataResolver.java | 6 +++++ .../model/auxiliary/ResolvedSurveyData.java | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/ResolvedSurveyData.java diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/testresult/ResultDataResolver.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/testresult/ResultDataResolver.java index 16c8db9f96..573628f349 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/testresult/ResultDataResolver.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/testresult/ResultDataResolver.java @@ -1,6 +1,7 @@ package gov.cdc.usds.simplereport.api.testresult; import gov.cdc.usds.simplereport.api.model.ApiFacility; +import gov.cdc.usds.simplereport.db.model.auxiliary.ResolvedSurveyData; import gov.cdc.usds.simplereport.db.model.auxiliary.TestResultsListItem; import org.springframework.graphql.data.method.annotation.SchemaMapping; import org.springframework.stereotype.Controller; @@ -12,4 +13,9 @@ public class ResultDataResolver { public ApiFacility getFacility(TestResultsListItem result) { return new ApiFacility(result.getFacility()); } + + @SchemaMapping(typeName = "Result", field = "surveyData") + public ResolvedSurveyData getSurveyData(TestResultsListItem result) { + return new ResolvedSurveyData(result.getSurveyData()); + } } diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/ResolvedSurveyData.java b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/ResolvedSurveyData.java new file mode 100644 index 0000000000..044e5e5c0e --- /dev/null +++ b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/ResolvedSurveyData.java @@ -0,0 +1,27 @@ +package gov.cdc.usds.simplereport.db.model.auxiliary; + +import java.time.LocalDate; +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ResolvedSurveyData { + + private String pregnancy; + private String syphilisHistory; + private String symptoms; + private Boolean noSymptoms; + private LocalDate symptomOnsetDate; + private List genderOfSexualPartners; + + public ResolvedSurveyData(AskOnEntrySurvey surveyData) { + this.pregnancy = surveyData.getPregnancy(); + this.syphilisHistory = surveyData.getSyphilisHistory(); + this.symptoms = surveyData.getSymptomsJSON(); + this.noSymptoms = surveyData.getNoSymptoms(); + this.symptomOnsetDate = surveyData.getSymptomOnsetDate(); + this.genderOfSexualPartners = surveyData.getGenderOfSexualPartners(); + } +} From 5b8f98c23f134d74cfe5e2df7e0acaaef62f50dc Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Tue, 17 Sep 2024 11:33:41 -0400 Subject: [PATCH 4/8] Use SymptomOnset instead of SymptomOnsetDate --- .../simplereport/db/model/auxiliary/ResolvedSurveyData.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/ResolvedSurveyData.java b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/ResolvedSurveyData.java index 044e5e5c0e..a69a8a0be8 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/ResolvedSurveyData.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/ResolvedSurveyData.java @@ -13,7 +13,7 @@ public class ResolvedSurveyData { private String syphilisHistory; private String symptoms; private Boolean noSymptoms; - private LocalDate symptomOnsetDate; + private LocalDate symptomOnset; private List genderOfSexualPartners; public ResolvedSurveyData(AskOnEntrySurvey surveyData) { @@ -21,7 +21,7 @@ public ResolvedSurveyData(AskOnEntrySurvey surveyData) { this.syphilisHistory = surveyData.getSyphilisHistory(); this.symptoms = surveyData.getSymptomsJSON(); this.noSymptoms = surveyData.getNoSymptoms(); - this.symptomOnsetDate = surveyData.getSymptomOnsetDate(); + this.symptomOnset = surveyData.getSymptomOnsetDate(); this.genderOfSexualPartners = surveyData.getGenderOfSexualPartners(); } } From 124d6fd72caae9fe65d81d7b429fbae62fb2d9b4 Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Thu, 19 Sep 2024 11:33:28 -0400 Subject: [PATCH 5/8] Add resolved patient link --- .../api/testresult/ResultDataResolver.java | 10 ++++++++++ .../db/model/auxiliary/TestResultsListItem.java | 2 ++ backend/src/main/resources/graphql/main.graphqls | 1 + 3 files changed, 13 insertions(+) diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/testresult/ResultDataResolver.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/testresult/ResultDataResolver.java index 573628f349..9ba23dacac 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/testresult/ResultDataResolver.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/testresult/ResultDataResolver.java @@ -1,8 +1,12 @@ package gov.cdc.usds.simplereport.api.testresult; import gov.cdc.usds.simplereport.api.model.ApiFacility; +import gov.cdc.usds.simplereport.db.model.PatientLink; import gov.cdc.usds.simplereport.db.model.auxiliary.ResolvedSurveyData; import gov.cdc.usds.simplereport.db.model.auxiliary.TestResultsListItem; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import org.dataloader.DataLoader; import org.springframework.graphql.data.method.annotation.SchemaMapping; import org.springframework.stereotype.Controller; @@ -18,4 +22,10 @@ public ApiFacility getFacility(TestResultsListItem result) { public ResolvedSurveyData getSurveyData(TestResultsListItem result) { return new ResolvedSurveyData(result.getSurveyData()); } + + @SchemaMapping(typeName = "Result", field = "patientLink") + public CompletableFuture getPatientLink( + TestResultsListItem result, DataLoader patientLinkDataLoader) { + return patientLinkDataLoader.load(result.getTestOrderId()); + } } diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/TestResultsListItem.java b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/TestResultsListItem.java index 823e47a2d9..87ce8d03bb 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/TestResultsListItem.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/auxiliary/TestResultsListItem.java @@ -24,6 +24,7 @@ public class TestResultsListItem { private final String reasonForCorrection; private final ApiUser createdBy; private final AskOnEntrySurvey surveyData; + private final UUID testOrderId; public TestResultsListItem(Result result) { this.id = result.getTestEvent().getInternalId(); @@ -39,5 +40,6 @@ public TestResultsListItem(Result result) { this.reasonForCorrection = result.getTestEvent().getReasonForCorrection(); this.createdBy = result.getTestEvent().getCreatedBy(); this.surveyData = result.getTestEvent().getSurveyData(); + this.testOrderId = result.getTestEvent().getTestOrderId(); } } diff --git a/backend/src/main/resources/graphql/main.graphqls b/backend/src/main/resources/graphql/main.graphqls index 127b088292..90ef1857fc 100644 --- a/backend/src/main/resources/graphql/main.graphqls +++ b/backend/src/main/resources/graphql/main.graphqls @@ -331,6 +331,7 @@ type Result { reasonForCorrection: String createdBy: ApiUser surveyData: AskOnEntrySurvey + patientLink: PatientLink } type ResultsPage { From 27e513ec04b2ae98601d87ad64642b0b74a1db98 Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Thu, 19 Sep 2024 11:33:55 -0400 Subject: [PATCH 6/8] Update TestResultTest --- .../api/graphql/TestResultTest.java | 27 +++++----- ...results-with-count-multiplex-query.graphql | 51 +++++++++++++------ .../test-results-with-count-query.graphql | 43 ++++++++++------ 3 files changed, 78 insertions(+), 43 deletions(-) diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/graphql/TestResultTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/graphql/TestResultTest.java index c6d7fc0f1b..4042d2fe5c 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/api/graphql/TestResultTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/graphql/TestResultTest.java @@ -177,18 +177,19 @@ void submitAndFetchMultiplexResult() { assertTrue(testResults.has(0), "Has at least one submitted test result="); assertEquals(dateTested, testResults.get(0).get("dateTested").asText()); - assertEquals("{\"25064002\":\"true\"}", testResults.get(0).get("symptoms").asText()); - assertEquals("false", testResults.get(0).get("noSymptoms").asText()); - assertEquals("77386006", testResults.get(0).get("pregnancy").asText()); - assertEquals("2020-09-15", testResults.get(0).get("symptomOnset").asText()); - assertEquals("[\"male\"]", testResults.get(0).get("genderOfSexualPartners").toString()); + assertEquals( + "{\"25064002\":\"true\"}", testResults.get(0).get("surveyData").get("symptoms").asText()); + assertEquals("false", testResults.get(0).get("surveyData").get("noSymptoms").asText()); + assertEquals("77386006", testResults.get(0).get("surveyData").get("pregnancy").asText()); + assertEquals("2020-09-15", testResults.get(0).get("surveyData").get("symptomOnset").asText()); + assertEquals( + "[\"male\"]", + testResults.get(0).get("surveyData").get("genderOfSexualPartners").toString()); testResults - .get(0) - .get("results") .elements() .forEachRemaining( r -> { - switch (r.get("disease").get("name").asText()) { + switch (r.get("disease").asText()) { case "COVID-19": assertEquals(TestResult.NEGATIVE.toString(), r.get("testResult").asText()); break; @@ -199,7 +200,7 @@ void submitAndFetchMultiplexResult() { assertEquals(TestResult.UNDETERMINED.toString(), r.get("testResult").asText()); break; default: - fail("Unexpected disease=" + r.get("disease").get("name").asText()); + fail("Unexpected disease=" + r.get("disease").asText()); } }); } @@ -279,8 +280,8 @@ void testResultOperations_standardUser_successDependsOnFacilityAccess() { updateSelfPrivileges(Role.USER, true, Set.of()); ArrayNode testResults = fetchTestResults(fetchVariables); assertEquals(2, testResults.size()); - UUID t1Id = UUID.fromString(testResults.get(0).get("internalId").asText()); - UUID t2Id = UUID.fromString(testResults.get(1).get("internalId").asText()); + UUID t1Id = UUID.fromString(testResults.get(0).get("id").asText()); + UUID t2Id = UUID.fromString(testResults.get(1).get("id").asText()); updateSelfPrivileges(Role.USER, false, Set.of(_secondSite.getInternalId())); @@ -506,13 +507,13 @@ private ObjectNode submitQueueItem( private ArrayNode fetchTestResults(Map variables) { return (ArrayNode) - runQuery("test-results-with-count-query", variables).get("testResultsPage").get("content"); + runQuery("test-results-with-count-query", variables).get("resultsPage").get("content"); } private ArrayNode fetchTestResultsMultiplex(Map variables) { return (ArrayNode) runQuery("test-results-with-count-multiplex-query", variables) - .get("testResultsPage") + .get("resultsPage") .get("content"); } diff --git a/backend/src/test/resources/graphql-test/test-results-with-count-multiplex-query.graphql b/backend/src/test/resources/graphql-test/test-results-with-count-multiplex-query.graphql index 1592d234c0..f35da6321b 100644 --- a/backend/src/test/resources/graphql-test/test-results-with-count-multiplex-query.graphql +++ b/backend/src/test/resources/graphql-test/test-results-with-count-multiplex-query.graphql @@ -1,15 +1,20 @@ query GetFacilityResultsWithCountMultiplex( $facilityId: ID! ) { - testResultsPage(facilityId: $facilityId) { + resultsPage( + facilityId: $facilityId + ) { content { - internalId + id + dateAdded dateTested - results { - disease { - name - } - testResult + disease + testResult + correctionStatus + reasonForCorrection + facility { + name + isDeleted } deviceType { internalId @@ -20,18 +25,34 @@ query GetFacilityResultsWithCountMultiplex( firstName middleName lastName - lookupId + birthDate + gender + role + email + phoneNumbers { + type + number + } } patientLink { internalId } - symptoms - noSymptoms - pregnancy - symptomOnset - genderOfSexualPartners + surveyData { + pregnancy + syphilisHistory + symptoms + symptomOnset + noSymptoms + genderOfSexualPartners + } + createdBy { + nameInfo { + firstName + middleName + lastName + } + } } - totalElements } -} +} \ No newline at end of file diff --git a/backend/src/test/resources/graphql-test/test-results-with-count-query.graphql b/backend/src/test/resources/graphql-test/test-results-with-count-query.graphql index 6a5f4224b6..63d0e5b87b 100644 --- a/backend/src/test/resources/graphql-test/test-results-with-count-query.graphql +++ b/backend/src/test/resources/graphql-test/test-results-with-count-query.graphql @@ -1,15 +1,18 @@ query GetFacilityResultsWithCount($facilityId: ID) { - testResultsPage(facilityId: $facilityId) { + resultsPage( + facilityId: $facilityId + ) { content { - internalId + id + dateAdded dateTested - results { - disease { - internalId - name - loinc - } - testResult + disease + testResult + correctionStatus + reasonForCorrection + facility { + name + isDeleted } deviceType { internalId @@ -20,16 +23,26 @@ query GetFacilityResultsWithCount($facilityId: ID) { firstName middleName lastName - lookupId + birthDate + gender + role + email + phoneNumbers { + type + number + } } patientLink { internalId } - symptoms - noSymptoms - pregnancy - symptomOnset + createdBy { + nameInfo { + firstName + middleName + lastName + } + } } totalElements } -} +} \ No newline at end of file From eca5da8ddf34afd92f2f001d438d70a1c2ae2693 Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Thu, 19 Sep 2024 11:44:44 -0400 Subject: [PATCH 7/8] Update codegen graphql --- frontend/src/generated/graphql.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/generated/graphql.tsx b/frontend/src/generated/graphql.tsx index bbb2a32f55..ff549e3888 100644 --- a/frontend/src/generated/graphql.tsx +++ b/frontend/src/generated/graphql.tsx @@ -931,6 +931,7 @@ export type Result = { facility: Facility; id: Scalars["ID"]["output"]; patient: Patient; + patientLink?: Maybe; reasonForCorrection?: Maybe; surveyData?: Maybe; testResult: Scalars["String"]["output"]; From da1d2572c4851cbe211320902af02fc9a94c98a3 Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Fri, 20 Sep 2024 13:05:52 -0400 Subject: [PATCH 8/8] Filter disabled diseases on csv download --- .../viewResults/DownloadResultsCsvModal.tsx | 7 +- frontend/src/app/utils/testResultCSV.test.ts | 18 ++- frontend/src/app/utils/testResultCSV.ts | 149 ++++++++++-------- 3 files changed, 104 insertions(+), 70 deletions(-) diff --git a/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.tsx b/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.tsx index 409905e885..0aa026db76 100644 --- a/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.tsx +++ b/frontend/src/app/testResults/viewResults/DownloadResultsCsvModal.tsx @@ -12,6 +12,7 @@ import { useGetResultsForDownloadLazyQuery, } from "../../../generated/graphql"; import { parseDataForCSV, ResultCsvRow } from "../../utils/testResultCSV"; +import { useDisabledFeatureDiseaseList } from "../../utils/disease"; import { ALL_FACILITIES_ID, ResultsQueryVariables } from "./TestResultsList"; @@ -37,6 +38,7 @@ export const DownloadResultsCsvModal = ({ >(null); // Disable downloads because backend will hang on over 20k results (#3953) const disableDownload = totalEntries > rowsMaxLimit; + const disabledFeatureDiseaseList = useDisabledFeatureDiseaseList(); const filtersPresent = Object.entries(filterParams).some(([key, val]) => { // active facility in the facility filter is the default @@ -67,7 +69,10 @@ export const DownloadResultsCsvModal = ({ const handleComplete = (data: GetResultsForDownloadQuery) => { if (data?.resultsPage?.content) { try { - const csvResults = parseDataForCSV(data.resultsPage.content); + const csvResults = parseDataForCSV( + data.resultsPage.content, + disabledFeatureDiseaseList + ); setResults(csvResults); } catch (e) { showError("Error creating results file to download"); diff --git a/frontend/src/app/utils/testResultCSV.test.ts b/frontend/src/app/utils/testResultCSV.test.ts index 0882bdc0b2..be2b2e5c6f 100644 --- a/frontend/src/app/utils/testResultCSV.test.ts +++ b/frontend/src/app/utils/testResultCSV.test.ts @@ -1,3 +1,5 @@ +import { MULTIPLEX_DISEASES } from "../testResults/constants"; + import { parseDataForCSV, QueriedTestResult } from "./testResultCSV"; const genericTestResult: Partial = { @@ -148,7 +150,7 @@ const covidResultRow = { Result: "NEGATIVE", }; -const csvResultRows = [ +const csvResultRowsNoSTD = [ { ...covidResultRow, }, @@ -167,6 +169,10 @@ const csvResultRows = [ Condition: "RSV", Result: "UNDETERMINED", }, +]; + +const csvResultRowsAllDiseases = [ + ...csvResultRowsNoSTD, { ...csvRowWithoutResult, Condition: "HIV", @@ -181,7 +187,7 @@ const csvResultRows = [ describe("parseDataForCSV", () => { it("parses multiplex data", () => { - expect(parseDataForCSV(data)).toEqual(csvResultRows); + expect(parseDataForCSV(data)).toEqual(csvResultRowsAllDiseases); }); it("parse data does not fail if tribalAffiliation is null", () => { expect( @@ -193,4 +199,12 @@ describe("parseDataForCSV", () => { ]) ).toEqual([covidResultRow]); }); + it("doesn't include disabled data if feature flags are false", () => { + expect( + parseDataForCSV(data, [ + MULTIPLEX_DISEASES.HIV, + MULTIPLEX_DISEASES.SYPHILIS, + ]) + ).toEqual(csvResultRowsNoSTD); + }); }); diff --git a/frontend/src/app/utils/testResultCSV.ts b/frontend/src/app/utils/testResultCSV.ts index 819d48566e..5605de0a8c 100644 --- a/frontend/src/app/utils/testResultCSV.ts +++ b/frontend/src/app/utils/testResultCSV.ts @@ -2,6 +2,7 @@ import moment from "moment"; import { byDateTested } from "../testResults/viewResults/TestResultsList"; import { GetResultsForDownloadQuery } from "../../generated/graphql"; +import { MULTIPLEX_DISEASES } from "../testResults/constants"; import { hasSymptomsForView, symptomsStringToArray } from "./symptoms"; @@ -71,75 +72,89 @@ export type ResultCsvRow = | undefined; }; -export function parseDataForCSV(data: QueriedTestResult[]): ResultCsvRow[] { +export function parseDataForCSV( + data: QueriedTestResult[], + disabledFeatureDiseaseList: MULTIPLEX_DISEASES[] = [] +): ResultCsvRow[] { let csvRows: ResultCsvRow[] = []; - data.sort(byDateTested).forEach((r: QueriedTestResult) => { - const symptomList = r?.surveyData?.symptoms - ? symptomsStringToArray(r.surveyData.symptoms) - : []; + data + .filter((r) => + disabledFeatureDiseaseList.every( + (d) => d.toLowerCase() !== r?.disease.toLowerCase() + ) + ) + .sort(byDateTested) + .forEach((r: QueriedTestResult) => { + const symptomList = r?.surveyData?.symptoms + ? symptomsStringToArray(r.surveyData.symptoms) + : []; - const swabTypes = r?.deviceType?.swabTypes ?? []; + const swabTypes = r?.deviceType?.swabTypes ?? []; - const resultRow: ResultCsvRow = { - "Patient first name": r?.patient?.firstName, - "Patient middle name": r?.patient?.middleName, - "Patient last name": r?.patient?.lastName, - "Patient full name": displayFullName( - r?.patient?.firstName, - r?.patient?.middleName, - r?.patient?.lastName - ), - "Patient date of birth": moment(r?.patient?.birthDate).format( - "MM/DD/YYYY" - ), - "Test date": moment(r?.dateTested).format("MM/DD/YYYY h:mma"), - Condition: r?.disease, - Result: r?.testResult, - "Result reported date": moment(r?.dateUpdated).format("MM/DD/YYYY h:mma"), - "Test correction status": r?.correctionStatus, - "Test correction reason": r?.reasonForCorrection, - "Device name": r?.deviceType?.name, - "Device manufacturer": r?.deviceType?.manufacturer, - "Device model": r?.deviceType?.model, - "Device swab type": swabTypes.length > 0 ? swabTypes[0].name : "", - "Has symptoms": hasSymptomsForView( - r?.surveyData?.noSymptoms ?? false, - r?.surveyData?.symptoms ?? "{}" - ), - "Symptoms present": - symptomList.length > 0 ? symptomList.join(", ") : "No symptoms", - "Symptom onset": moment(r?.surveyData?.symptomOnset).format("MM/DD/YYYY"), - "Facility name": facilityDisplayName( - r?.facility?.name ?? "", - r?.facility?.isDeleted ?? false - ), - Submitter: displayFullName( - r?.createdBy?.nameInfo?.firstName, - r?.createdBy?.nameInfo?.middleName, - r?.createdBy?.nameInfo?.lastName - ), - "Patient role": r?.patient?.role, - "Patient ID (Student ID, Employee ID, etc.)": r?.patient?.lookupId, - "Patient preferred language": r?.patient?.preferredLanguage, - "Patient phone number": r?.patient?.phoneNumbers?.at(0)?.number ?? "", - "Patient email": r?.patient?.email, - "Patient street address": r?.patient?.street, - "Patient street address 2": r?.patient?.streetTwo, - "Patient city": r?.patient?.city, - "Patient state": r?.patient?.state, - "Patient zip code": r?.patient?.zipCode, - "Patient county": r?.patient?.county, - "Patient country": r?.patient?.country, - "Patient gender": r?.patient?.gender, - "Patient race": r?.patient?.race, - "Patient ethnicity": r?.patient?.ethnicity, - "Patient tribal affiliation": - r?.patient?.tribalAffiliation?.join(", ") || "", - "Patient is a resident in a congregate setting": - r?.patient?.residentCongregateSetting, - "Patient is employed in healthcare": r?.patient?.employedInHealthcare, - }; - csvRows.push(resultRow); - }); + const resultRow: ResultCsvRow = { + "Patient first name": r?.patient?.firstName, + "Patient middle name": r?.patient?.middleName, + "Patient last name": r?.patient?.lastName, + "Patient full name": displayFullName( + r?.patient?.firstName, + r?.patient?.middleName, + r?.patient?.lastName + ), + "Patient date of birth": moment(r?.patient?.birthDate).format( + "MM/DD/YYYY" + ), + "Test date": moment(r?.dateTested).format("MM/DD/YYYY h:mma"), + Condition: r?.disease, + Result: r?.testResult, + "Result reported date": moment(r?.dateUpdated).format( + "MM/DD/YYYY h:mma" + ), + "Test correction status": r?.correctionStatus, + "Test correction reason": r?.reasonForCorrection, + "Device name": r?.deviceType?.name, + "Device manufacturer": r?.deviceType?.manufacturer, + "Device model": r?.deviceType?.model, + "Device swab type": swabTypes.length > 0 ? swabTypes[0].name : "", + "Has symptoms": hasSymptomsForView( + r?.surveyData?.noSymptoms ?? false, + r?.surveyData?.symptoms ?? "{}" + ), + "Symptoms present": + symptomList.length > 0 ? symptomList.join(", ") : "No symptoms", + "Symptom onset": moment(r?.surveyData?.symptomOnset).format( + "MM/DD/YYYY" + ), + "Facility name": facilityDisplayName( + r?.facility?.name ?? "", + r?.facility?.isDeleted ?? false + ), + Submitter: displayFullName( + r?.createdBy?.nameInfo?.firstName, + r?.createdBy?.nameInfo?.middleName, + r?.createdBy?.nameInfo?.lastName + ), + "Patient role": r?.patient?.role, + "Patient ID (Student ID, Employee ID, etc.)": r?.patient?.lookupId, + "Patient preferred language": r?.patient?.preferredLanguage, + "Patient phone number": r?.patient?.phoneNumbers?.at(0)?.number ?? "", + "Patient email": r?.patient?.email, + "Patient street address": r?.patient?.street, + "Patient street address 2": r?.patient?.streetTwo, + "Patient city": r?.patient?.city, + "Patient state": r?.patient?.state, + "Patient zip code": r?.patient?.zipCode, + "Patient county": r?.patient?.county, + "Patient country": r?.patient?.country, + "Patient gender": r?.patient?.gender, + "Patient race": r?.patient?.race, + "Patient ethnicity": r?.patient?.ethnicity, + "Patient tribal affiliation": + r?.patient?.tribalAffiliation?.join(", ") || "", + "Patient is a resident in a congregate setting": + r?.patient?.residentCongregateSetting, + "Patient is employed in healthcare": r?.patient?.employedInHealthcare, + }; + csvRows.push(resultRow); + }); return csvRows; }