diff --git a/containers/ecr-viewer/README.md b/containers/ecr-viewer/README.md index 05076e425..fb5d24f66 100644 --- a/containers/ecr-viewer/README.md +++ b/containers/ecr-viewer/README.md @@ -50,6 +50,8 @@ If you have problems connecting to your database, use this command to see what o If building the Docker Image doesn't work as expected, try to first run the eCR Viewer locally using the steps below. +If you consistently encounter the error message `"ecr_viewer_db" does not exist` when attempting to run the app, there could be conflicting databases running on port 5432 as part of other background processes. Try pruning any dangling Docker images and containers (`docker image prune` and `docker container prune`). If issues persist, try logging into `psql` on the command line to see what databases are running there. + ## Development ### Run eCR Viewer Locally diff --git a/containers/ecr-viewer/src/app/services/ecrMetadataService.ts b/containers/ecr-viewer/src/app/services/ecrMetadataService.ts index d33fc8902..263c261d6 100644 --- a/containers/ecr-viewer/src/app/services/ecrMetadataService.ts +++ b/containers/ecr-viewer/src/app/services/ecrMetadataService.ts @@ -222,12 +222,7 @@ const evaluateEcrAuthorDetails = ( authorDetails.push([ { title: "Author Name", - value: formatName( - practitioner?.name?.[0].given, - practitioner?.name?.[0].family, - practitioner?.name?.[0].prefix, - practitioner?.name?.[0].suffix, - ), + value: formatName(practitioner?.name?.[0]), }, { title: "Author Address", diff --git a/containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts b/containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts index ee4ce49df..f50adbea2 100644 --- a/containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts +++ b/containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts @@ -5,6 +5,7 @@ import { Coding, Condition, Encounter, + HumanName, Location, Organization, PatientContact, @@ -43,35 +44,21 @@ export const evaluatePatientName = ( mappings: PathMappings, isPatientBanner: boolean, ) => { - const nameList = evaluate(fhirBundle, mappings.patientNameList); - - if (nameList.length > 0) { - if (isPatientBanner) { - const name = nameList.find((name) => name.use === "official"); - if (name) { - return formatName(name.given, name.family, name.prefix, name.suffix); - } else { - return formatName( - nameList[0].given, - nameList[0].family, - nameList[0].prefix, - nameList[0].suffix, - ); - } - } else { - return nameList - .map((name) => { - return formatName( - name.given, - name.family, - name.prefix, - name.suffix, - nameList.length > 1 ? name?.use : undefined, - ); - }) - .join("\n"); - } + const nameList: HumanName[] = evaluate(fhirBundle, mappings.patientNameList); + + // Return early if there's no name + if (nameList.length === 0) { + return; + } + + if (isPatientBanner) { + const officialName = nameList.find((n) => n.use === "official"); + return formatName(officialName ?? nameList[0]); } + + return nameList + .map((name) => formatName(name, nameList.length > 1)) + .join("\n"); }; /** @@ -471,12 +458,7 @@ export const evaluateProviderData = ( const providerData = [ { title: "Provider Name", - value: formatName( - practitioner?.name?.[0].given, - practitioner?.name?.[0].family, - practitioner?.name?.[0].prefix, - practitioner?.name?.[0].suffix, - ), + value: formatName(practitioner?.name?.[0]), }, { title: "Provider Address", @@ -535,13 +517,7 @@ export const evaluateEncounterCareTeamTable = ( return { Name: { - value: - formatName( - practitioner?.name?.[0].given, - practitioner?.name?.[0].family, - practitioner?.name?.[0].prefix, - practitioner?.name?.[0].suffix, - ) || noData, + value: formatName(practitioner?.name?.[0]) || noData, }, Role: { value: role || noData, diff --git a/containers/ecr-viewer/src/app/services/formatService.tsx b/containers/ecr-viewer/src/app/services/formatService.tsx index 10a4603a0..b11512df9 100644 --- a/containers/ecr-viewer/src/app/services/formatService.tsx +++ b/containers/ecr-viewer/src/app/services/formatService.tsx @@ -1,6 +1,6 @@ import React from "react"; import { ToolTipElement } from "@/app/view-data/components/ToolTipElement"; -import { Address, ContactPoint } from "fhir/r4"; +import { Address, ContactPoint, HumanName } from "fhir/r4"; import { RenderableNode, safeParse } from "../view-data/utils/utils"; import sanitizeHtml from "sanitize-html"; @@ -22,39 +22,30 @@ export interface TableJson { } /** - * Formats a person's name using given name(s), family name, optional prefix(es), and optional suffix(es). - * @param given - Optional array of given name(s). - * @param family - Optional string representing family name or surname. - * @param [prefix] - Optional array of name prefix(es). - * @param [suffix] - Optional array of name suffix(es). - * @param [use] - Optional array of name use(s). - * @returns Formatted name. + * Formats a person's name: : . + * @param humanName - The HumanName object containing the name components. + * @param withUse - Whether to include the name use in the formatted string. + * @returns The formatted name string. */ export const formatName = ( - given?: string[], - family?: string, - prefix?: string[], - suffix?: string[], - use?: string, -) => { - const nameArray: string[] = []; - if (use) { - nameArray.push(toSentenceCase(use) + ":"); - } - if (prefix) { - nameArray.push(...prefix); - } - if (given) { - nameArray.push(...given); - } - if (family) { - nameArray.push(family); - } - if (suffix) { - nameArray.push(...suffix); + humanName: HumanName | undefined, + withUse: boolean = false, +): string => { + if (!humanName) { + return ""; } - return nameArray.join(" ").trim(); + const { use, prefix, given, family, suffix } = humanName; + + const segments = [ + ...(withUse && use ? [`${toSentenceCase(use)}:`] : []), + ...(prefix ?? []), + ...(given ?? []), + family ?? "", + ...(suffix ?? []), + ]; + + return segments.filter(Boolean).join(" "); }; const DEFAULT_ADDRESS_CONFIG = { includeUse: false, includePeriod: false }; diff --git a/containers/ecr-viewer/src/app/tests/services/formatService.test.tsx b/containers/ecr-viewer/src/app/tests/services/formatService.test.tsx index 57dc424a5..87c09aaf7 100644 --- a/containers/ecr-viewer/src/app/tests/services/formatService.test.tsx +++ b/containers/ecr-viewer/src/app/tests/services/formatService.test.tsx @@ -15,38 +15,41 @@ import { formatAddress, formatPhoneNumber, } from "@/app/services/formatService"; -import { ContactPoint } from "fhir/r4"; +import { ContactPoint, HumanName } from "fhir/r4"; describe("Format Name", () => { - const inputGiven = ["Gregory", "B"]; - const inputFamily = "House"; + const inputHumanName = { + given: ["Gregory", "B"], + family: "House", + } as HumanName; it("should return only given and family name", () => { const expectedName = "Gregory B House"; - const result = formatName(inputGiven, inputFamily); + const result = formatName(inputHumanName); expect(result).toEqual(expectedName); }); it("should return the prefix, given, family, and suffix names", () => { - const inputPrefix = ["Dr."]; - const inputSuffix = ["III"]; const expectedName = "Dr. Gregory B House III"; - const result = formatName( - inputGiven, - inputFamily, - inputPrefix, - inputSuffix, - ); + inputHumanName.prefix = ["Dr."]; + inputHumanName.suffix = ["III"]; + + const result = formatName(inputHumanName); expect(result).toEqual(expectedName); }); it("should return an empty string", () => { - const inputEmpty: any[] = []; + const emptyHumanName = { + given: [], + family: "", + prefix: [], + suffix: [], + } as HumanName; const expectedName = ""; - const result = formatName(inputEmpty, "", inputEmpty, inputEmpty); + const result = formatName(emptyHumanName); expect(result).toEqual(expectedName); }); }); diff --git a/containers/ecr-viewer/src/app/view-data/components/common.tsx b/containers/ecr-viewer/src/app/view-data/components/common.tsx index 68b632b57..05e667d3a 100644 --- a/containers/ecr-viewer/src/app/view-data/components/common.tsx +++ b/containers/ecr-viewer/src/app/view-data/components/common.tsx @@ -94,12 +94,7 @@ export const returnCareTeamTable = ( (nameObject) => nameObject.family, ); if (entry.member) { - (entry.member as any).name = formatName( - practitionerNameObj?.given, - practitionerNameObj?.family, - practitionerNameObj?.prefix, - practitionerNameObj?.suffix, - ); + (entry.member as any).name = formatName(practitionerNameObj); } }); return (