From 881e39ef33e2dd1be1738c8ede4d683f226a3fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Zakrzewski?= <41862803+stanislaw-zakrzewski@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:56:30 +0100 Subject: [PATCH 1/3] [98] add query for curator comments (#154) * Allow search for curators comment * Fix highlighting of the text in view case form * Update DataGuideDialog.tsx * Cleanup * Inform user if there is any not closed quote * Cleanup code + remove console.log * Add cypress tests for comments and uneven quote warning * WIP fixed issues with filters and special characters in free text search * Update index.tsx * Update SearchBar.tsx * Update index.tsx * Fix setting filters * Fix issues with text-search not visible * Cleanup * Cleanup SearchBar * Remove console.log --- .../setup-db/schemas/day0cases.indexes.json | 13 +++- .../e2e/components/LinelistTableTest.spec.ts | 27 +++++++ .../ui/cypress/support/commands.ts | 2 + .../ui/src/components/App/index.tsx | 6 +- .../ui/src/components/DataGuideDialog.tsx | 55 +++++++++++--- .../ui/src/components/FiltersDialog/index.tsx | 14 +++- .../ui/src/components/SearchBar.tsx | 57 ++++++++------- .../ui/src/components/ViewCase.tsx | 73 ++++++++++++------- 8 files changed, 178 insertions(+), 69 deletions(-) diff --git a/data-serving/scripts/setup-db/schemas/day0cases.indexes.json b/data-serving/scripts/setup-db/schemas/day0cases.indexes.json index a7007c6b6..b55dd708a 100644 --- a/data-serving/scripts/setup-db/schemas/day0cases.indexes.json +++ b/data-serving/scripts/setup-db/schemas/day0cases.indexes.json @@ -8,7 +8,8 @@ "location.admin2": "text", "location.admin3": "text", "caseReference.sourceUrl": "text", - "caseStatus": "text" + "caseStatus": "text", + "comment": "text" } }, { @@ -145,6 +146,16 @@ "strength": 2 } }, + { + "name": "commentIdx", + "key": { + "comment": -1 + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, { "name": "countryAndDate", "key": { diff --git a/verification/curator-service/ui/cypress/e2e/components/LinelistTableTest.spec.ts b/verification/curator-service/ui/cypress/e2e/components/LinelistTableTest.spec.ts index 7cc942796..be520b375 100644 --- a/verification/curator-service/ui/cypress/e2e/components/LinelistTableTest.spec.ts +++ b/verification/curator-service/ui/cypress/e2e/components/LinelistTableTest.spec.ts @@ -501,6 +501,7 @@ describe('Linelist table', function () { sourceUrl: 'www.example.com', caseStatus: CaseStatus.Confirmed, occupation: 'Actor', + comment: 'note', }); cy.addCase({ country: 'Germany', @@ -524,6 +525,7 @@ describe('Linelist table', function () { cy.intercept('GET', getDefaultQuery({ limit: 50 })).as('getCases'); cy.intercept('GET', getDefaultQuery({ limit: 50, query: 'Argentina' })).as('getCasesWithSearch1'); cy.intercept('GET', getDefaultQuery({ limit: 50, query: 'Doctor' })).as('getCasesWithSearch2'); + cy.intercept('GET', getDefaultQuery({ limit: 50, query: 'note' })).as('getCasesWithSearch3'); cy.visit('/cases'); cy.wait('@getCases'); @@ -549,5 +551,30 @@ describe('Linelist table', function () { cy.contains('Argentina').should('not.exist'); cy.contains('France').should('not.exist'); cy.contains('Germany').should('exist'); + + cy.get('#clear-search').click(); + cy.wait('@getCases'); + cy.contains('Argentina').should('exist'); + cy.contains('France').should('exist'); + cy.contains('Germany').should('exist'); + + cy.get('#search-field').type('note'); + cy.wait('@getCasesWithSearch3'); + cy.contains('Argentina').should('not.exist'); + cy.contains('France').should('exist'); + cy.contains('Germany').should('not.exist'); + }); + + it('Informs user when uneven number of quotes is present in free-text search', () => { + cy.intercept('GET', getDefaultQuery({ limit: 50, query: '"Bus driver"' })).as('getCasesWithSearch'); + cy.visit('/cases'); + cy.contains('Please make sure you have an even number of quotes.').should('not.exist'); + + cy.get('#search-field').type('"Bus driver'); + cy.contains('Please make sure you have an even number of quotes.').should('exist'); + + cy.get('#search-field').type('"'); + cy.wait('@getCasesWithSearch'); + cy.contains('Please make sure you have an even number of quotes.').should('not.exist'); }); }); diff --git a/verification/curator-service/ui/cypress/support/commands.ts b/verification/curator-service/ui/cypress/support/commands.ts index 4ddc86446..c951c0be8 100644 --- a/verification/curator-service/ui/cypress/support/commands.ts +++ b/verification/curator-service/ui/cypress/support/commands.ts @@ -43,6 +43,7 @@ interface AddCaseProps { gender?: Gender; outcome?: Outcome; uploadIds?: string[]; + comment?: string; } declare global { @@ -111,6 +112,7 @@ export function addCase(opts: AddCaseProps): void { travelHistory: {}, genomeSequences: {}, vaccination: {}, + comment: opts.comment || '', }, }); } diff --git a/verification/curator-service/ui/src/components/App/index.tsx b/verification/curator-service/ui/src/components/App/index.tsx index 0cc3fe81d..44c0e58c4 100644 --- a/verification/curator-service/ui/src/components/App/index.tsx +++ b/verification/curator-service/ui/src/components/App/index.tsx @@ -327,13 +327,14 @@ export default function App(): JSX.Element { }; const onModalClose = (): void => { + const searchQueryObject = new URLSearchParams(searchQuery); navigate( { pathname: location.state && location.state.lastLocation ? location.state.lastLocation : '/cases', - search: searchQuery, + search: searchQueryObject.toString(), }, { state: { lastLocation: '/case/view' } }, ); @@ -371,7 +372,8 @@ export default function App(): JSX.Element { ) return; - dispatch(setSearchQuery(location.search)); + const searchParams = new URLSearchParams(location.search); + dispatch(setSearchQuery(searchParams.toString())); // Save searchQuery to local storage not to lost it when user goes through auth process localStorage.setItem('searchQuery', location.search); diff --git a/verification/curator-service/ui/src/components/DataGuideDialog.tsx b/verification/curator-service/ui/src/components/DataGuideDialog.tsx index d24c7a03c..8a3c92801 100644 --- a/verification/curator-service/ui/src/components/DataGuideDialog.tsx +++ b/verification/curator-service/ui/src/components/DataGuideDialog.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import { Box, Portal, Theme, Typography } from '@mui/material'; import { withStyles } from 'tss-react/mui'; -import CloseIcon from '@mui/icons-material/Close'; +import { Close as CloseIcon } from '@mui/icons-material'; import Draggable, { ControlPosition } from 'react-draggable'; // As per this issue from react-draggable library: https://github.com/react-grid-layout/react-draggable/pull/648 @@ -96,16 +96,49 @@ const SearchGuideDialog = ({ the left and choose ascending or descending. - For full-text search, enter any combination of search terms. -
- Full-text search covers: occupation, admin0, admin1, admin2, admin3, sourceUrl and caseStatus. -
- Search terms must be exact (example: "German" will not match "Germany"). -
- Full-text search matches cases that contain any ot the search terms, not a combination. -
- No special characters apart from "." are allowed and if the "." is used in a search term - given search term must be contained within quotation marks. + For full-text search, enter any + combination of search terms. Rules for full-text + search: +
+
You can use the icons on the right to navigate diff --git a/verification/curator-service/ui/src/components/FiltersDialog/index.tsx b/verification/curator-service/ui/src/components/FiltersDialog/index.tsx index 810a28c8d..a18d3c69e 100644 --- a/verification/curator-service/ui/src/components/FiltersDialog/index.tsx +++ b/verification/curator-service/ui/src/components/FiltersDialog/index.tsx @@ -17,7 +17,7 @@ import { useMediaQuery, } from '@mui/material'; import { Close as CloseIcon } from '@mui/icons-material'; -import { filtersToURL, URLToFilters } from '../util/searchQuery'; +import { URLToFilters } from '../util/searchQuery'; import { hasAnyRole } from '../util/helperFunctions'; import { useAppSelector, useAppDispatch } from '../../hooks/redux'; import { fetchCountries } from '../../redux/filters/thunk'; @@ -153,13 +153,19 @@ export default function FiltersDialog({ handleSetModalAlert(); dispatch(setModalOpen(false)); - const searchQuery = filtersToURL(values); + const searchParams = new URLSearchParams(); + for (const [key, value] of Object.entries(values)) { + if (value) searchParams.set(key, value); + } + const q = (new URLSearchParams(location.search)).get('q') + if (q) searchParams.set('q', q); + const searchParamsString = searchParams.toString(); - sendCustomGtmEvent('filters_applied', { query: searchQuery }); + sendCustomGtmEvent('filters_applied', { query: searchParamsString }); navigate({ pathname: '/cases', - search: searchQuery, + search: searchParamsString, }); }, }); diff --git a/verification/curator-service/ui/src/components/SearchBar.tsx b/verification/curator-service/ui/src/components/SearchBar.tsx index a4ebf82e2..f45287740 100644 --- a/verification/curator-service/ui/src/components/SearchBar.tsx +++ b/verification/curator-service/ui/src/components/SearchBar.tsx @@ -15,7 +15,6 @@ import clsx from 'clsx'; import DataGuideDialog from './DataGuideDialog'; import { useDebounce } from '../hooks/useDebounce'; import FiltersDialog from './FiltersDialog'; -import { searchQueryToURL, URLToSearchQuery } from './util/searchQuery'; import { useLocation, useNavigate } from 'react-router-dom'; import { KeyboardEvent, ChangeEvent } from 'react'; import { useAppSelector, useAppDispatch } from '../hooks/redux'; @@ -85,9 +84,7 @@ export default function SearchBar({ const [isUserTyping, setIsUserTyping] = useState(false); const [isDataGuideOpen, setIsDataGuideOpen] = useState(false); const [searchInput, setSearchInput] = useState( - location.search.includes('?q=') - ? URLToSearchQuery(location.search) - : '', + new URLSearchParams(location.search).get('q') || '', ); const [modalAlert, setModalAlert] = useState(false); const guideButtonRef = React.useRef(null); @@ -103,28 +100,30 @@ export default function SearchBar({ } }, [filtersBreadcrumb]); + useEffect(() => { + const q = new URLSearchParams(location.search).get('q') || ''; + if (q !== searchInput) setSearchInput(q); + }, [location.search]); + // Set search query debounce to 1000ms const debouncedSearch = useDebounce(searchInput, 2000); - // Update search input based on search query - useEffect(() => { - if (!location.search.includes('?q=')) { - setSearchInput(''); - return; - } + const handleNavigating = (q: string) => { + const searchParams = new URLSearchParams(location.search); + q !== '' ? searchParams.set('q', q) : searchParams.delete('q'); - setSearchInput(URLToSearchQuery(location.search)); - }, [location.search]); + navigate({ + pathname: '/cases', + search: searchParams.toString(), + }); + }; // Apply filter parameters after delay useEffect(() => { if (!isUserTyping) return; - setIsUserTyping(false); - navigate({ - pathname: '/cases', - search: searchQueryToURL(debouncedSearch), - }); + + handleNavigating(debouncedSearch); //eslint-disable-next-line }, [debouncedSearch]); @@ -136,10 +135,8 @@ export default function SearchBar({ if (ev.key === 'Enter') { ev.preventDefault(); setIsUserTyping(false); - navigate({ - pathname: '/cases', - search: searchQueryToURL(searchInput), - }); + + handleNavigating(searchInput); } }; @@ -174,16 +171,24 @@ export default function SearchBar({ return searchStringStrippedOutColon; } + const renderSearchErrorMessage = () => { + if (searchError) { + return 'Incorrect entry. ":" characters have been removed. Please use filters instead.'; + } else { + const quoteCount = searchInput.split('"').length - 1; + if (quoteCount % 2 !== 0) { + return 'Incorrect entry. Please make sure you have an even number of quotes.'; + } + } + }; + return ( <>
({ errorMessage: { @@ -98,14 +101,14 @@ export default function ViewCase(props: Props): JSX.Element { {loading && } {errorMessage && ( - {errorMessage} - + )} {c && ( { + subs != '' && i % 2 ? quoted.push(subs) : notQuoted.push(subs); + }); + } else notQuoted.push(q); + + const regex = /"([^"]+)"|(\S{1,})/g; + // Make sure that terms in quotes will be highlighted as one search term + for (const quotedEntry of quoted) { + let match; + let accumulator: string[] = []; + while ((match = regex.exec(quotedEntry))) { + accumulator.push(match[match[1] ? 1 : 2]); + } + searchQueryArray.push(accumulator.join(' ')); + } + for (const notQuotedEntry of notQuoted) { + let match; + while ((match = regex.exec(notQuotedEntry))) { + searchQueryArray.push(match[match[1] ? 1 : 2]); + } } - return searchQueryArray; } words(searchQuery); From 8db28d3d61d82e516d37453fc849cf4e0d03c81e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Zakrzewski?= <41862803+stanislaw-zakrzewski@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:11:42 +0100 Subject: [PATCH 2/3] [104] change date format (#155) * Format date properly for csv, tsv and json download * Update tests * Use dedicated function for date formatting * Remove only from tests --- .../data-service/src/controllers/case.ts | 8 +++ data-serving/data-service/src/util/case.ts | 65 +++++++++++-------- .../data-service/test/util/case.test.ts | 27 ++++++-- 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/data-serving/data-service/src/controllers/case.ts b/data-serving/data-service/src/controllers/case.ts index 02ce0eaef..11e59454b 100644 --- a/data-serving/data-service/src/controllers/case.ts +++ b/data-serving/data-service/src/controllers/case.ts @@ -352,6 +352,14 @@ export class CasesController { header: false, columns: this.csvHeaders, delimiter: delimiter, + cast: { + date: (value: Date) => { + if (value) { + return new Date(value).toISOString().split('T')[0]; + } + return value; + }, + } }); res.write(stringifiedCase); doc = await cursor.next(); diff --git a/data-serving/data-service/src/util/case.ts b/data-serving/data-service/src/util/case.ts index db8faba83..3989908f0 100644 --- a/data-serving/data-service/src/util/case.ts +++ b/data-serving/data-service/src/util/case.ts @@ -103,6 +103,11 @@ export const removeBlankHeader = (headers: string[]): string[] => { return headers; }; +export const formatDateWithoutTime = (date: Date | undefined): string => { + if (!date) return ''; + return date.toISOString().split('T')[0]; +}; + export const denormalizeFields = async ( doc: CaseDocument, ): Promise> => { @@ -178,9 +183,8 @@ function denormalizeCaseReferenceFields( additionalSources.push(source.sourceUrl); } } - denormalizedData[ - 'caseReference.additionalSources' - ] = additionalSources.join(','); + denormalizedData['caseReference.additionalSources'] = + additionalSources.join(','); denormalizedData['caseReference.sourceEntryId'] = doc.sourceEntryId || ''; denormalizedData['caseReference.sourceId'] = doc.sourceId || ''; denormalizedData['caseReference.sourceUrl'] = doc.sourceUrl || ''; @@ -211,38 +215,46 @@ export const denormalizeEventsFields = ( const denormalizedData: Record = {}; denormalizedData['events.dateEntry'] = doc.dateEntry - ? doc.dateEntry.toDateString() + ? formatDateWithoutTime(doc.dateEntry) : undefined || ''; denormalizedData['events.dateReported'] = doc.dateReported - ? doc.dateReported.toDateString() + ? formatDateWithoutTime(doc.dateReported) : undefined || ''; - denormalizedData['events.dateOnset'] = doc.dateOnset?.toDateString() || ''; - denormalizedData['events.dateConfirmation'] = - doc.dateConfirmation?.toDateString() || ''; + denormalizedData['events.dateOnset'] = formatDateWithoutTime(doc.dateOnset); + denormalizedData['events.dateConfirmation'] = formatDateWithoutTime( + doc.dateConfirmation, + ); denormalizedData['events.confirmationMethod'] = doc.confirmationMethod || ''; - denormalizedData['events.dateOfFirstConsult'] = - doc.dateOfFirstConsult?.toDateString() || ''; + denormalizedData['events.dateOfFirstConsult'] = formatDateWithoutTime( + doc.dateOfFirstConsult, + ); denormalizedData['events.hospitalized'] = doc.hospitalized || ''; denormalizedData['events.reasonForHospitalization'] = doc.reasonForHospitalization || ''; - denormalizedData['events.dateHospitalization'] = - doc.dateHospitalization?.toDateString() || ''; - denormalizedData['events.dateDischargeHospital'] = - doc.dateDischargeHospital?.toDateString() || ''; + denormalizedData['events.dateHospitalization'] = formatDateWithoutTime( + doc.dateHospitalization, + ); + denormalizedData['events.dateDischargeHospital'] = formatDateWithoutTime( + doc.dateDischargeHospital, + ); denormalizedData['events.intensiveCare'] = doc.intensiveCare || ''; - denormalizedData['events.dateAdmissionICU'] = - doc.dateAdmissionICU?.toDateString() || ''; - denormalizedData['events.dateDischargeICU'] = - doc.dateDischargeICU?.toDateString() || ''; + denormalizedData['events.dateAdmissionICU'] = formatDateWithoutTime( + doc.dateAdmissionICU, + ); + denormalizedData['events.dateDischargeICU'] = formatDateWithoutTime( + doc.dateDischargeICU, + ); denormalizedData['events.homeMonitoring'] = doc.homeMonitoring || ''; denormalizedData['events.isolated'] = doc.isolated || ''; - denormalizedData['events.dateIsolation'] = - doc.dateIsolation?.toDateString() || ''; + denormalizedData['events.dateIsolation'] = formatDateWithoutTime( + doc.dateIsolation, + ); denormalizedData['events.outcome'] = doc.outcome || ''; - denormalizedData['events.dateDeath'] = doc.dateDeath?.toDateString() || ''; - denormalizedData['events.dateRecovered'] = - doc.dateRecovered?.toDateString() || ''; + denormalizedData['events.dateDeath'] = formatDateWithoutTime(doc.dateDeath); + denormalizedData['events.dateRecovered'] = formatDateWithoutTime( + doc.dateRecovered, + ); return denormalizedData; }; @@ -334,7 +346,7 @@ function denormalizeTravelHistoryFields( denormalizedData['travelHistory.travelHistory'] = doc?.travelHistory || ''; denormalizedData['travelHistory.travelHistoryEntry'] = - doc?.travelHistoryEntry?.toDateString() || ''; + formatDateWithoutTime(doc?.travelHistoryEntry); denormalizedData['travelHistory.travelHistoryStart'] = doc?.travelHistoryStart || ''; denormalizedData['travelHistory.travelHistoryLocation'] = @@ -352,8 +364,9 @@ function denormalizeVaccineFields( denormalizedData['vaccination.vaccination'] = doc?.vaccination || ''; denormalizedData['vaccination.vaccineName'] = doc?.vaccineName || ''; - denormalizedData['vaccination.vaccineDate'] = - doc?.vaccineDate?.toDateString() || ''; + denormalizedData['vaccination.vaccineDate'] = formatDateWithoutTime( + doc?.vaccineDate, + ); denormalizedData['vaccination.vaccineSideEffects'] = doc?.vaccineSideEffects || ''; diff --git a/data-serving/data-service/test/util/case.test.ts b/data-serving/data-service/test/util/case.test.ts index 205bcb7d7..b43715dc5 100644 --- a/data-serving/data-service/test/util/case.test.ts +++ b/data-serving/data-service/test/util/case.test.ts @@ -13,7 +13,11 @@ import { RevisionMetadataDocument } from '../model/revision-metadata'; import { TransmissionDocument } from '../model/transmission'; import { TravelHistoryDocument } from '../model/travel-history'; import { VaccineDocument } from '../model/vaccine'; -import { removeBlankHeader, denormalizeFields } from '../../src/util/case'; +import { + removeBlankHeader, + denormalizeFields, + formatDateWithoutTime, +} from '../../src/util/case'; import mongoose from 'mongoose'; import { MongoMemoryServer } from 'mongodb-memory-server'; import { GenomeSequenceDocument } from '../../src/model/genome-sequence'; @@ -421,16 +425,16 @@ describe('Case', () => { const denormalizedCase = await denormalizeFields(caseDoc); expect(denormalizedCase['events.dateEntry']).toEqual( - eventsDoc.dateEntry.toDateString(), + formatDateWithoutTime(eventsDoc.dateEntry), ); expect(denormalizedCase['events.dateReported']).toEqual( - eventsDoc.dateReported.toDateString(), + formatDateWithoutTime(eventsDoc.dateReported), ); expect(denormalizedCase['events.dateOnset']).toEqual( - eventsDoc.dateOnset?.toDateString(), + formatDateWithoutTime(eventsDoc.dateOnset), ); expect(denormalizedCase['events.dateConfirmation']).toEqual( - eventsDoc.dateConfirmation?.toDateString(), + formatDateWithoutTime(eventsDoc.dateConfirmation), ); expect(denormalizedCase['events.confirmationMethod']).toEqual(''); expect(denormalizedCase['events.dateOfFirstConsult']).toEqual(''); @@ -612,7 +616,7 @@ describe('Case', () => { expect(denormalizedCase['travelHistory.travelHistory']).toEqual('Y'); expect(denormalizedCase['travelHistory.travelHistoryEntry']).toEqual( - travelHistoryDoc.travelHistoryEntry.toDateString(), + formatDateWithoutTime(travelHistoryDoc.travelHistoryEntry), ); expect(denormalizedCase['travelHistory.travelHistoryStart']).toEqual( 'start', @@ -652,7 +656,7 @@ describe('Case', () => { expect(denormalizedCase['vaccination.vaccination']).toEqual('Y'); expect(denormalizedCase['vaccination.vaccineName']).toEqual('Pfizer'); expect(denormalizedCase['vaccination.vaccineDate']).toEqual( - vaccinationDoc.vaccineDate.toDateString(), + formatDateWithoutTime(vaccinationDoc.vaccineDate), ); expect(denormalizedCase['vaccination.vaccineSideEffects']).toEqual( 'cough', @@ -689,4 +693,13 @@ describe('Case', () => { '1234', ); }); + + it('formatsDateWithoutTimeCorrectly', async () => { + const correctDateString = '2024-01-01'; + const correctDate = new Date(`${correctDateString}T03:24:00`); + const missingDate = undefined; + + expect(formatDateWithoutTime(correctDate)).toEqual(correctDateString); + expect(formatDateWithoutTime(missingDate)).toEqual(''); + }); }); From 4fb80a378d24b32dd14eb4bdbdeb09fe37a53694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Zakrzewski?= <41862803+stanislaw-zakrzewski@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:10:55 +0100 Subject: [PATCH 3/3] [105] na fields (#158) * Update UI YesNo enums to remove Na * Update Curator.spec.ts * Update data service * Update openapi.yaml * Update dockerfile * Update configuration for CI tests * Update make_superuser.sh --- data-serving/data-service/api/openapi.yaml | 33 ++++----- data-serving/data-service/src/types/enums.ts | 2 +- .../data-service/test/util/case.test.ts | 25 ++++--- dev/make_superuser.sh | 2 +- geocoding/location-service/Dockerfile | 4 +- geocoding/location-service/Dockerfile-test | 2 +- .../curator-service/api/openapi/openapi.yaml | 33 ++++----- .../ui/cypress/e2e/components/Curator.spec.ts | 73 +++++++++++-------- .../ui/cypress/support/commands.ts | 2 +- .../curator-service/ui/src/api/models/Case.ts | 4 +- .../ui/src/api/models/Day0Case.ts | 2 +- .../common-form-fields/FormikFields.tsx | 2 +- .../ui/src/components/fixtures/fullCase.json | 2 +- .../new-case-form-fields/CaseFormValues.tsx | 3 +- .../new-case-form-fields/Demographics.tsx | 4 +- .../new-case-form-fields/Events.tsx | 28 ++++--- .../PreexistingConditions.tsx | 8 +- .../new-case-form-fields/Transmission.tsx | 4 +- .../new-case-form-fields/TravelHistory.tsx | 7 +- .../new-case-form-fields/Vaccines.tsx | 18 ++--- 20 files changed, 129 insertions(+), 129 deletions(-) diff --git a/data-serving/data-service/api/openapi.yaml b/data-serving/data-service/api/openapi.yaml index 6bd8d5df7..5cb28cf97 100644 --- a/data-serving/data-service/api/openapi.yaml +++ b/data-serving/data-service/api/openapi.yaml @@ -656,6 +656,9 @@ paths: $ref: '#/components/responses/500' components: schemas: + YesNo: + type: string + enum: ['Y', 'N', ''] BatchUpdateResponse: description: Response to batch update cases API requests properties: @@ -766,8 +769,7 @@ components: occupation: type: string healthcareWorker: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' location: $ref: '#/components/schemas/Location' events: @@ -788,8 +790,7 @@ components: dateOfFirstConsult: $ref: '#/components/schemas/Date' hospitalized: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' reasonForHospitalization: type: string enum: [monitoring, treatment, unknown] @@ -798,18 +799,15 @@ components: dateDischargeHospital: $ref: '#/components/schemas/Date' intensiveCare: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' dateAdmissionICU: $ref: '#/components/schemas/Date' dateDischargeICU: $ref: '#/components/schemas/Date' homeMonitoring: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' isolated: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' dateIsolation: $ref: '#/components/schemas/Date' outcome: @@ -825,21 +823,18 @@ components: type: object properties: previousInfection: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' coInfection: type: string preexistingCondition: type: string pregnancyStatus: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' transmission: type: object properties: contactWithCase: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' contactId: type: string contactSetting: @@ -854,8 +849,7 @@ components: type: object properties: travelHistory: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' travelHistoryEntry: $ref: '#/components/schemas/Date' travelHistoryStart: @@ -875,8 +869,7 @@ components: type: object properties: vaccination: - type: string - enum: ['Y', 'N', 'NA'] + $ref: '#/components/schemas/YesNo' vaccineName: type: string vaccineDate: diff --git a/data-serving/data-service/src/types/enums.ts b/data-serving/data-service/src/types/enums.ts index 56d5ef12c..e6d5ed844 100644 --- a/data-serving/data-service/src/types/enums.ts +++ b/data-serving/data-service/src/types/enums.ts @@ -8,7 +8,7 @@ export enum CaseStatus { export enum YesNo { Y = 'Y', N = 'N', - NA = 'NA', + None = '', } export enum Role { diff --git a/data-serving/data-service/test/util/case.test.ts b/data-serving/data-service/test/util/case.test.ts index b43715dc5..fcc1108a4 100644 --- a/data-serving/data-service/test/util/case.test.ts +++ b/data-serving/data-service/test/util/case.test.ts @@ -22,6 +22,7 @@ import mongoose from 'mongoose'; import { MongoMemoryServer } from 'mongodb-memory-server'; import { GenomeSequenceDocument } from '../../src/model/genome-sequence'; import { EventsDocument } from '../../src/model/events'; +import { YesNo } from '../../src/types/enums'; let mongoServer: MongoMemoryServer; @@ -368,7 +369,7 @@ describe('Case', () => { ageBuckets: [anAgeBucket._id], gender: 'male', occupation: 'Anesthesiologist', - healthcareWorker: 'Y', + healthcareWorker: YesNo.Y, } as DemographicsDocument; const caseDoc = { @@ -395,7 +396,7 @@ describe('Case', () => { expect(denormalizedCase['demographics.occupation']).toEqual( 'Anesthesiologist', ); - expect(denormalizedCase['demographics.healthcareWorker']).toEqual('Y'); + expect(denormalizedCase['demographics.healthcareWorker']).toEqual(YesNo.Y); }); it('denormalizes events fields', async () => { const eventsDoc = { @@ -507,10 +508,10 @@ describe('Case', () => { }); it('denormalizes preexisting conditions fields', async () => { const conditionsDoc = { - previousInfection: 'Y', + previousInfection: YesNo.Y, coInfection: 'Flu', preexistingCondition: '', - pregnancyStatus: 'NA', + pregnancyStatus: YesNo.None, } as PreexistingConditionsDocument; const caseDoc = { @@ -533,7 +534,7 @@ describe('Case', () => { const denormalizedCase = await denormalizeFields(caseDoc); expect( denormalizedCase['preexistingConditions.previousInfection'], - ).toEqual('Y'); + ).toEqual(YesNo.Y); expect(denormalizedCase['preexistingConditions.coInfection']).toEqual( 'Flu', ); @@ -542,11 +543,11 @@ describe('Case', () => { ).toEqual(''); expect( denormalizedCase['preexistingConditions.pregnancyStatus'], - ).toEqual('NA'); + ).toEqual(YesNo.None); }); it('denormalizes transmission fields', async () => { const transmissionDoc = { - contactWithCase: 'Y', + contactWithCase: YesNo.Y, contactId: 'abc123', contactSetting: 'setting', contactAnimal: 'animal', @@ -572,7 +573,7 @@ describe('Case', () => { const denormalizedCase = await denormalizeFields(caseDoc); - expect(denormalizedCase['transmission.contactWithCase']).toEqual('Y'); + expect(denormalizedCase['transmission.contactWithCase']).toEqual(YesNo.Y); expect(denormalizedCase['transmission.contactId']).toEqual('abc123'); expect(denormalizedCase['transmission.contactSetting']).toEqual( 'setting', @@ -589,7 +590,7 @@ describe('Case', () => { }); it('denormalizes travel history fields', async () => { const travelHistoryDoc = { - travelHistory: 'Y', + travelHistory: YesNo.Y, travelHistoryEntry: new Date('2020-11-01'), travelHistoryStart: 'start', travelHistoryLocation: 'London', @@ -614,7 +615,7 @@ describe('Case', () => { const denormalizedCase = await denormalizeFields(caseDoc); - expect(denormalizedCase['travelHistory.travelHistory']).toEqual('Y'); + expect(denormalizedCase['travelHistory.travelHistory']).toEqual(YesNo.Y); expect(denormalizedCase['travelHistory.travelHistoryEntry']).toEqual( formatDateWithoutTime(travelHistoryDoc.travelHistoryEntry), ); @@ -630,7 +631,7 @@ describe('Case', () => { }); it('denormalizes vaccine fields', async () => { const vaccinationDoc = { - vaccination: 'Y', + vaccination: YesNo.Y, vaccineName: 'Pfizer', vaccineDate: new Date('2020-11-01'), vaccineSideEffects: 'cough', @@ -653,7 +654,7 @@ describe('Case', () => { } as CaseDocument; const denormalizedCase = await denormalizeFields(caseDoc); - expect(denormalizedCase['vaccination.vaccination']).toEqual('Y'); + expect(denormalizedCase['vaccination.vaccination']).toEqual(YesNo.Y); expect(denormalizedCase['vaccination.vaccineName']).toEqual('Pfizer'); expect(denormalizedCase['vaccination.vaccineDate']).toEqual( formatDateWithoutTime(vaccinationDoc.vaccineDate), diff --git a/dev/make_superuser.sh b/dev/make_superuser.sh index e60d1e35e..5ff05f6f5 100755 --- a/dev/make_superuser.sh +++ b/dev/make_superuser.sh @@ -10,6 +10,6 @@ cd `dirname "$0"` # Pass database name as the first parameter DB="${GDH_DATABASE:-$1}" # Pass email of user to grant an admin role as second parameter -docker-compose -f docker-compose.yml -f docker-compose.dev.yml exec mongo mongosh "${DB}" --eval "var email='$2'; var roles=['admin', 'curator'];" /verification/scripts/roles.js +docker compose -f docker-compose.yml -f docker-compose.dev.yml exec mongo mongosh "${DB}" --eval "var email='$2'; var roles=['admin', 'curator'];" /verification/scripts/roles.js # Restore directory. popd \ No newline at end of file diff --git a/geocoding/location-service/Dockerfile b/geocoding/location-service/Dockerfile index 7b156c704..03b99b645 100644 --- a/geocoding/location-service/Dockerfile +++ b/geocoding/location-service/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10-slim +FROM python:3.10-bullseye RUN apt-get update -y RUN apt-get install -y python3-pip @@ -14,7 +14,7 @@ WORKDIR /usr/src/app/geocoding/location-service USER flask # install dependencies -RUN pip install poetry +RUN pip install poetry==1.8.5 COPY geocoding/location-service/pyproject.toml . COPY geocoding/location-service/poetry.lock . RUN /home/flask/.local/bin/poetry install diff --git a/geocoding/location-service/Dockerfile-test b/geocoding/location-service/Dockerfile-test index 7c7a80aa2..0a346eea5 100644 --- a/geocoding/location-service/Dockerfile-test +++ b/geocoding/location-service/Dockerfile-test @@ -1,5 +1,5 @@ # `python-base` sets up all our shared environment variables -FROM python:3.10-slim as python-base +FROM python:3.10-bullseye as python-base ENV PYTHONUNBUFFERED=1 \ # prevents python creating .pyc files diff --git a/verification/curator-service/api/openapi/openapi.yaml b/verification/curator-service/api/openapi/openapi.yaml index 062408b0f..ac7241369 100644 --- a/verification/curator-service/api/openapi/openapi.yaml +++ b/verification/curator-service/api/openapi/openapi.yaml @@ -1283,6 +1283,9 @@ paths: components: schemas: + YesNo: + type: string + enum: [ 'Y', 'N', '' ] Parser: description: A parser for automated ingestion type: object @@ -1593,8 +1596,7 @@ components: occupation: type: string healthcareWorker: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' location: $ref: '#/components/schemas/Location' events: @@ -1615,8 +1617,7 @@ components: dateOfFirstConsult: $ref: '#/components/schemas/Date' hospitalized: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' reasonForHospitalization: type: string enum: [monitoring, treatment, unknown] @@ -1625,18 +1626,15 @@ components: dateDischargeHospital: $ref: '#/components/schemas/Date' intensiveCare: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' dateAdmissionICU: $ref: '#/components/schemas/Date' dateDischargeICU: $ref: '#/components/schemas/Date' homeMonitoring: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' isolated: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' dateIsolation: $ref: '#/components/schemas/Date' outcome: @@ -1653,22 +1651,19 @@ components: type: object properties: previousInfection: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' coInfection: type: string preexistingCondition: type: string pregnancyStatus: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' transmission: description: How this case got infected and by who if known type: object properties: contactWithCase: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' contactId: type: string contactSetting: @@ -1684,8 +1679,7 @@ components: description: Travel history of the patient if known properties: travelHistory: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' travelHistoryEntry: $ref: '#/components/schemas/Date' travelHistoryStart: @@ -1709,8 +1703,7 @@ components: type: object properties: vaccination: - type: string - enum: ['Y', 'N', 'NA', undefined] + $ref: '#/components/schemas/YesNo' vaccineName: type: string vaccineDate: diff --git a/verification/curator-service/ui/cypress/e2e/components/Curator.spec.ts b/verification/curator-service/ui/cypress/e2e/components/Curator.spec.ts index ab24ff822..1007db534 100644 --- a/verification/curator-service/ui/cypress/e2e/components/Curator.spec.ts +++ b/verification/curator-service/ui/cypress/e2e/components/Curator.spec.ts @@ -1,4 +1,6 @@ /* eslint-disable no-undef */ +import { YesNo } from '../../support/commands'; + describe('Curator', function () { beforeEach(() => { cy.task('clearCasesDB', {}); @@ -56,27 +58,35 @@ describe('Curator', function () { // GENERAL cy.get('div[data-testid="caseStatus"]').click(); cy.get('li[data-value="confirmed"').click(); - cy.get('div[data-testid="comment"]').type('This case should be consulted with Supervisor.'); + cy.get('div[data-testid="comment"]').type( + 'This case should be consulted with Supervisor.', + ); // DATA SOURCE cy.get('div[data-testid="caseReference"]').type('www.example.com'); cy.contains('www.example.com').click(); - cy.get('input[name="caseReference.isGovernmentSource"]').click() + cy.get('input[name="caseReference.isGovernmentSource"]').click(); cy.get('button[data-testid="add-additional-source"]').click(); - cy.get('div[data-testid="additional-source-0"]').type('www.example2.com'); + cy.get('div[data-testid="additional-source-0"]').type( + 'www.example2.com', + ); cy.get('button[data-testid="add-additional-source"]').click(); - cy.get('div[data-testid="additional-source-1"]').type('www.example3.com'); + cy.get('div[data-testid="additional-source-1"]').type( + 'www.example3.com', + ); cy.get('span[data-testid="government-source-1"]').click(); // LOCATION - cy.get('div[data-testid="location.geocodeLocation"]').type('France', { delay: 0}); + cy.get('div[data-testid="location.geocodeLocation"]').type('France', { + delay: 0, + }); cy.wait('@geolocationFranceSuggest'); cy.contains('France').click(); /* Change France to something else to check we can edit geocode results. * We need to change it to a valid country so that we can find the ISO code! */ - cy.get('div[data-testid="location.country"]').click() - cy.get('input[name="location.country"]').clear().type('Germany') + cy.get('div[data-testid="location.country"]').click(); + cy.get('input[name="location.country"]').clear().type('Germany'); cy.get('p').contains('Germany').click(); cy.get('input[name="location.admin1"]').type('Berlin'); cy.get('p').contains('Berlin').click(); @@ -84,7 +94,9 @@ describe('Curator', function () { cy.get('p').contains('SK Berlin').click(); cy.get('input[name="location.admin3"]').type('Berlin'); cy.get('p').contains('Berlin').click(); - cy.get('div[data-testid="location.comment"]').type('Martin Luther Hospital'); + cy.get('div[data-testid="location.comment"]').type( + 'Martin Luther Hospital', + ); // EVENTS cy.get('input[name="events.dateEntry"]').type('2020-01-01'); @@ -94,16 +106,16 @@ describe('Curator', function () { cy.get('input[name="events.dateOnset"]').type('2020-01-03'); cy.get('input[name="events.dateOfFirstConsult"]').type('2020-01-04'); cy.get('div[data-testid="events.homeMonitoring"').click(); - cy.get('li[data-value="Y"]').click(); + cy.get(`li[data-value="${YesNo.Y}"]`).click(); cy.get('div[data-testid="events.isolated"]').click(); - cy.get('li[data-value="Y"]').click(); + cy.get(`li[data-value="${YesNo.Y}"]`).click(); cy.get('input[name="events.dateIsolation"]').type('2020-01-05'); cy.get('div[data-testid="events.hospitalized"]').click(); - cy.get('li[data-value="Y"]').click(); + cy.get(`li[data-value="${YesNo.Y}"]`).click(); cy.get('input[name="events.dateHospitalization"]').type('2020-01-06'); cy.get('input[name="events.dateDischargeHospital"]').type('2020-01-07'); cy.get('div[data-testid="events.intensiveCare"]').click(); - cy.get('li[data-value="Y"]').click(); + cy.get(`li[data-value="${YesNo.Y}"]`).click(); cy.get('input[name="events.dateAdmissionICU"]').type('2020-01-08'); cy.get('input[name="events.dateDischargeICU"]').type('2020-01-09'); cy.get('div[data-testid="events.outcome"]').click(); @@ -117,7 +129,7 @@ describe('Curator', function () { cy.get('div[data-testid="occupation"]').click(); cy.contains('li', 'Accountant').click(); cy.get('div[data-testid="demographics.healthcareWorker"]').click(); - cy.get('li[data-value="N"]').click(); + cy.get(`li[data-value="${YesNo.N}"]`).click(); // SYMPTOMS cy.get('div[data-testid="symptoms"]').type('dry cough'); @@ -129,7 +141,7 @@ describe('Curator', function () { cy.get( 'div[data-testid="preexistingConditions.previousInfection"]', ).click(); - cy.get('li[data-value="Y"').click(); + cy.get(`li[data-value="${YesNo.Y}"]`).click(); cy.get('input[name="preexistingConditions.coInfection"]').type('Flu'); cy.get('div[data-testid="preexistingConditionsHelper"]').type( 'ABCD syndrome', @@ -142,11 +154,11 @@ describe('Curator', function () { cy.get( 'div[data-testid="preexistingConditions.pregnancyStatus"]', ).click(); - cy.get('li[data-value="N"]').click(); + cy.get(`li[data-value="${YesNo.N}"]`).click(); // TRANSMISSION cy.get('div[data-testid="transmission.contactWithCase"]').click(); - cy.get('li[data-value="Y"]').click(); + cy.get(`li[data-value="${YesNo.Y}"]`).click(); cy.get('input[name="transmission.contactId"]').type('ABC123'); cy.get('input[name="transmission.contactSetting"]').type( 'test setting', @@ -160,7 +172,7 @@ describe('Curator', function () { // TRAVEL HISTORY cy.get('div[data-testid="travelHistory.travelHistory"]').click(); - cy.get('li[data-value="Y"]').click(); + cy.get(`li[data-value="${YesNo.Y}"]`).click(); cy.get('input[name="travelHistory.travelHistoryEntry"]').type( '2020-02-01', ); @@ -184,7 +196,7 @@ describe('Curator', function () { // VACCINES cy.get('div[data-testid="vaccination.vaccination"]').click(); - cy.get('li[data-value="Y"]').click(); + cy.get(`li[data-value="${YesNo.Y}"]`).click(); cy.get('input[name="vaccination.vaccineName"]').type('Moderna'); cy.get('input[name="vaccination.vaccineDate"]').type('2020-03-01'); cy.get('div[data-testid="vaccineSideEffects"]').click(); @@ -268,7 +280,7 @@ describe('Curator', function () { 'have.value', 'Berlin', ); - cy.get('input[value="Martin Luther Hospital"]').should("exist"); + cy.get('input[value="Martin Luther Hospital"]').should('exist'); // Events. cy.get('input[name="events.dateEntry"]').should( @@ -297,16 +309,19 @@ describe('Curator', function () { ); cy.get('input[name="events.homeMonitoring"]').should( 'have.value', - 'Y', + YesNo.Y, + ); + cy.get('input[name="events.isolated"]').should( + 'have.value', + YesNo.Y, ); - cy.get('input[name="events.isolated"]').should('have.value', 'Y'); cy.get('input[name="events.dateIsolation"]').should( 'have.value', '2020/01/05', ); cy.get('input[name="events.hospitalized"]').should( 'have.value', - 'Y', + YesNo.Y, ); cy.get('input[name="events.dateHospitalization"]').should( 'have.value', @@ -318,7 +333,7 @@ describe('Curator', function () { ); cy.get('input[name="events.intensiveCare"]').should( 'have.value', - 'Y', + YesNo.Y, ); cy.get('input[name="events.dateAdmissionICU"]').should( 'have.value', @@ -344,7 +359,7 @@ describe('Curator', function () { // Preconditions. cy.get( 'input[name="preexistingConditions.previousInfection"]', - ).should('have.value', 'Y'); + ).should('have.value', YesNo.Y); cy.get('input[name="preexistingConditions.coInfection"]').should( 'have.value', 'Flu', @@ -353,12 +368,12 @@ describe('Curator', function () { cy.contains('ADULT syndrome'); cy.get( 'input[name="preexistingConditions.pregnancyStatus"]', - ).should('have.value', 'N'); + ).should('have.value', YesNo.N); // Transmission cy.get('input[name="transmission.contactWithCase"]').should( 'have.value', - 'Y', + YesNo.Y, ); cy.get('input[name="transmission.contactId"]').should( 'have.value', @@ -386,7 +401,7 @@ describe('Curator', function () { // Travel history. cy.get('input[name="travelHistory.travelHistory"]').should( 'have.value', - 'Y', + YesNo.Y, ); cy.get('input[name="travelHistory.travelHistoryEntry"]').should( 'have.value', @@ -408,7 +423,7 @@ describe('Curator', function () { // Vaccination cy.get('input[name="vaccination.vaccination"]').should( 'have.value', - 'Y', + YesNo.Y, ); cy.get('input[name="vaccination.vaccineName"]').should( 'have.value', @@ -428,7 +443,7 @@ describe('Curator', function () { cy.get('input[type="text"]').clear().type('Test occupation'); }); cy.contains('li', 'Test occupation').click(); - cy.get('div[data-testid="location.comment"]').clear() + cy.get('div[data-testid="location.comment"]').clear(); cy.contains('li', 'Martin Luther Hospital').click(); // Submit the changes. cy.get('button[data-testid="submit"]').click(); diff --git a/verification/curator-service/ui/cypress/support/commands.ts b/verification/curator-service/ui/cypress/support/commands.ts index c951c0be8..ce7c0dbf4 100644 --- a/verification/curator-service/ui/cypress/support/commands.ts +++ b/verification/curator-service/ui/cypress/support/commands.ts @@ -12,7 +12,7 @@ export enum Outcome { export enum YesNo { Y = 'Y', N = 'N', - NA = 'NA', + None = '', } export enum Gender { diff --git a/verification/curator-service/ui/src/api/models/Case.ts b/verification/curator-service/ui/src/api/models/Case.ts index 65a113dea..dbcf13d9f 100644 --- a/verification/curator-service/ui/src/api/models/Case.ts +++ b/verification/curator-service/ui/src/api/models/Case.ts @@ -1,4 +1,6 @@ // Case definitions as defined by the /api/cases endpoint. +import {YesNo} from "./Day0Case"; + export enum VerificationStatus { Unverified = 'UNVERIFIED', Verified = 'VERIFIED', @@ -132,7 +134,7 @@ export interface Vaccine { batch: string; date: Date; sideEffects: Symptoms; - previousInfection: 'yes' | 'no' | 'NA'; + previousInfection: YesNo; previousInfectionDetectionMethod: string; } diff --git a/verification/curator-service/ui/src/api/models/Day0Case.ts b/verification/curator-service/ui/src/api/models/Day0Case.ts index 434a47f30..c56030ca1 100644 --- a/verification/curator-service/ui/src/api/models/Day0Case.ts +++ b/verification/curator-service/ui/src/api/models/Day0Case.ts @@ -14,7 +14,7 @@ export enum Gender { export enum YesNo { Y = 'Y', N = 'N', - NA = 'NA', + None = '', } export enum HospitalizationReason { diff --git a/verification/curator-service/ui/src/components/common-form-fields/FormikFields.tsx b/verification/curator-service/ui/src/components/common-form-fields/FormikFields.tsx index 783ff5bcf..15b61bb6d 100644 --- a/verification/curator-service/ui/src/components/common-form-fields/FormikFields.tsx +++ b/verification/curator-service/ui/src/components/common-form-fields/FormikFields.tsx @@ -173,7 +173,7 @@ export function SelectField(props: SelectFieldProps): JSX.Element { > {props.values.map((value) => ( - {value} + {value === '' ? None : value} ))} diff --git a/verification/curator-service/ui/src/components/fixtures/fullCase.json b/verification/curator-service/ui/src/components/fixtures/fullCase.json index 8d3f915d9..cc5730249 100644 --- a/verification/curator-service/ui/src/components/fixtures/fullCase.json +++ b/verification/curator-service/ui/src/components/fixtures/fullCase.json @@ -37,7 +37,7 @@ "symptoms": "Severe pneumonia, Dyspnea, Weakness", "preexistingConditions": { "previousInfection": "N", - "pregnancyStatus": "NA" + "pregnancyStatus": "" }, "transmission": { "contactWithCase": "N" diff --git a/verification/curator-service/ui/src/components/new-case-form-fields/CaseFormValues.tsx b/verification/curator-service/ui/src/components/new-case-form-fields/CaseFormValues.tsx index e40bf2e3f..ab9a71f1c 100644 --- a/verification/curator-service/ui/src/components/new-case-form-fields/CaseFormValues.tsx +++ b/verification/curator-service/ui/src/components/new-case-form-fields/CaseFormValues.tsx @@ -1,5 +1,6 @@ import { CaseReferenceForm } from '../common-form-fields/Source'; import { Location as Loc } from '../../api/models/Case'; +import { YesNo } from '../../api/models/Day0Case'; /** * CaseFormValues defines all the values contained in the manual case entry form. @@ -88,7 +89,7 @@ export interface Vaccine { batch?: string; date?: Date; sideEffects: Symptom; - previousInfection: 'yes' | 'no' | 'NA'; + previousInfection: YesNo; previousInfectionDetectionMethod?: string; } diff --git a/verification/curator-service/ui/src/components/new-case-form-fields/Demographics.tsx b/verification/curator-service/ui/src/components/new-case-form-fields/Demographics.tsx index 66baef092..3fcd37454 100644 --- a/verification/curator-service/ui/src/components/new-case-form-fields/Demographics.tsx +++ b/verification/curator-service/ui/src/components/new-case-form-fields/Demographics.tsx @@ -12,7 +12,7 @@ import { TextField } from 'formik-mui'; import { StyledTooltip } from './StyledTooltip'; import axios from 'axios'; import { makeStyles } from 'tss-react/mui'; -import { Gender, Day0CaseFormValues } from '../../api/models/Day0Case'; +import { Gender, Day0CaseFormValues, YesNo } from '../../api/models/Day0Case'; import { useStyles } from './styled'; const styles = makeStyles()(() => ({ @@ -169,7 +169,7 @@ export default function Demographics(): JSX.Element { ); diff --git a/verification/curator-service/ui/src/components/new-case-form-fields/Events.tsx b/verification/curator-service/ui/src/components/new-case-form-fields/Events.tsx index c4da29e8e..73032ace3 100644 --- a/verification/curator-service/ui/src/components/new-case-form-fields/Events.tsx +++ b/verification/curator-service/ui/src/components/new-case-form-fields/Events.tsx @@ -1,15 +1,13 @@ import clsx from 'clsx'; -import { FastField, useFormikContext } from 'formik'; -import { TextField } from 'formik-mui'; +import {FastField, useFormikContext} from 'formik'; +import {TextField} from 'formik-mui'; import Scroll from 'react-scroll'; -import { Outcome, Day0CaseFormValues } from '../../api/models/Day0Case'; -import { DateField, SelectField } from '../common-form-fields/FormikFields'; +import {Day0CaseFormValues, Outcome, YesNo} from '../../api/models/Day0Case'; +import {DateField, SelectField} from '../common-form-fields/FormikFields'; import FieldTitle from '../common-form-fields/FieldTitle'; -import { useStyles } from './styled'; -import { StyledTooltip } from './StyledTooltip'; - -const yesNoUndefined = ['Y', 'N', 'NA']; +import {useStyles} from './styled'; +import {StyledTooltip} from './StyledTooltip'; const outcomes = ['recovered', 'death']; @@ -184,14 +182,14 @@ export default function Events(): JSX.Element { - {values.events.isolated === 'Y' && ( + {values.events.isolated === YesNo.Y && ( - {values.events.hospitalized === 'Y' && ( + {values.events.hospitalized === YesNo.Y && ( <> - {values.events.intensiveCare === 'Y' && ( + {values.events.intensiveCare === YesNo.Y && ( <> (
    @@ -60,7 +58,7 @@ export default function PreexistingConditions(): JSX.Element {
    @@ -87,7 +85,7 @@ export default function PreexistingConditions(): JSX.Element { ); diff --git a/verification/curator-service/ui/src/components/new-case-form-fields/Transmission.tsx b/verification/curator-service/ui/src/components/new-case-form-fields/Transmission.tsx index 300e00c82..77d5585ce 100644 --- a/verification/curator-service/ui/src/components/new-case-form-fields/Transmission.tsx +++ b/verification/curator-service/ui/src/components/new-case-form-fields/Transmission.tsx @@ -3,7 +3,7 @@ import { FastField, useFormikContext } from 'formik'; import { TextField } from 'formik-mui'; import Scroll from 'react-scroll'; -import { Day0CaseFormValues } from '../../api/models/Day0Case'; +import { Day0CaseFormValues, YesNo } from '../../api/models/Day0Case'; import FieldTitle from '../common-form-fields/FieldTitle'; import { FormikAutocomplete, @@ -54,7 +54,7 @@ export default function Transmission(): JSX.Element {
    ( @@ -59,9 +58,9 @@ export default function TravelHistory(): JSX.Element { - {values.travelHistory.travelHistory === 'Y' && ( + {values.travelHistory.travelHistory === YesNo.Y && ( <> ( @@ -45,9 +45,9 @@ export default function Vaccines(): JSX.Element { - {values.vaccination.vaccination === 'Y' && ( + {values.vaccination.vaccination === YesNo.Y && ( <>