Skip to content

Commit

Permalink
Fix field standardization in eCR Library (#3160)
Browse files Browse the repository at this point in the history
* add toTitleCase, patient and emg contact name

* make emg contact relationship title case

* make demographics, sex title case

* make demographics, sex title case

* apply title case to address lines and cities

* update facility contact format in summary

* email should be lowercase

* Change toTitleCase func to const

Co-authored-by: Mary McGrath <[email protected]>

* [pre-commit.ci] auto fixes from pre-commit hooks

* add unit tests for single-character and multi-line strings

---------

Co-authored-by: Mary McGrath <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 21, 2025
1 parent 4d6f649 commit 49222c5
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 38 deletions.
10 changes: 8 additions & 2 deletions containers/ecr-viewer/src/app/services/ecrSummaryService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
formatAddress,
formatContactPoint,
formatDate,
formatPhoneNumber,
formatStartEndDateTime,
toTitleCase,
} from "@/app/services/formatService";
import { evaluate } from "@/app/view-data/utils/evaluate";
import {
Expand Down Expand Up @@ -48,7 +50,9 @@ export const evaluateEcrSummaryPatientDetails = (
},
{
title: "Sex",
value: evaluate(fhirBundle, fhirPathMappings.patientGender)[0],
value: toTitleCase(
evaluate(fhirBundle, fhirPathMappings.patientGender)[0],
),
},
{
title: "Patient Address",
Expand Down Expand Up @@ -122,7 +126,9 @@ export const evaluateEcrSummaryEncounterDetails = (
},
{
title: "Facility Contact",
value: evaluate(fhirBundle, fhirPathMappings.facilityContact),
value: formatPhoneNumber(
evaluate(fhirBundle, fhirPathMappings.facilityContact)[0],
),
},
]);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
formatPhoneNumber,
formatStartEndDate,
formatStartEndDateTime,
toTitleCase,
} from "./formatService";
import fhirpath_r4_model from "fhirpath/fhir-context/r4";
import { Element } from "fhir/r4";
Expand Down Expand Up @@ -328,7 +329,10 @@ export const evaluateDemographicsData = (
title: "Date of Death",
value: evaluate(fhirBundle, mappings.patientDOD)[0],
},
{ title: "Sex", value: evaluate(fhirBundle, mappings.patientGender)[0] },
{
title: "Sex",
value: toTitleCase(evaluate(fhirBundle, mappings.patientGender)[0]),
},
{
title: "Race",
value: evaluatePatientRace(fhirBundle, mappings),
Expand Down Expand Up @@ -596,7 +600,9 @@ export const evaluateEmergencyContact = (

return contacts
.map((contact) => {
const relationship = contact.relationship?.[0].coding?.[0]?.display;
const relationship = toSentenceCase(
contact.relationship?.[0].coding?.[0]?.display,
);

const contactName = contact.name ? formatName(contact.name) : "";

Expand Down
24 changes: 18 additions & 6 deletions containers/ecr-viewer/src/app/services/formatService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ export const formatName = (

const segments = [
...(withUse && use ? [`${toSentenceCase(use)}:`] : []),
...(prefix ?? []),
...(given ?? []),
family ?? "",
...(prefix?.map(toTitleCase) ?? []),
...(given?.map(toTitleCase) ?? []),
toTitleCase(family ?? ""),
...(suffix ?? []),
];

Expand Down Expand Up @@ -84,8 +84,8 @@ export const formatAddress = (

return [
includeUse && use && toSentenceCase(use) + ":",
(line || []).filter(Boolean).join("\n"),
[city, state].filter(Boolean).join(", "),
(line?.map(toTitleCase) || []).filter(Boolean).join("\n"),
[toTitleCase(city), state].filter(Boolean).join(", "),
[postalCode, country].filter(Boolean).join(", "),
includePeriod && formatDateLine(),
]
Expand Down Expand Up @@ -592,6 +592,18 @@ export function toSentenceCase(str: string | undefined) {
if (!str) return str;
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
/**
* Converts a string to title case, making the first character of each word uppercase.
* @param str - The string to convert to title case.
* @returns The converted title-case string. If the input is empty or not a string, the original input is returned.
*/
export const toTitleCase = (str: string | undefined) => {
if (!str) return str;
return str.replace(
/\w\S*/g,
(text) => text.charAt(0).toUpperCase() + text.substring(1).toLowerCase(),
);
};

/**
* Adds a caption to a table element.
Expand Down Expand Up @@ -680,7 +692,7 @@ export const formatContactPoint = (
[phoneNumberUse, formatPhoneNumber(value)].filter((c) => c).join(": "),
);
} else if (system === "email") {
contactArr.push(value);
contactArr.push(value.toLowerCase());
} else {
const _use = toSentenceCase(use ?? "");
const _system = toSentenceCase(system ?? "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -933,9 +933,9 @@ Burbank, CA
<div
class="grid-col maxw7 text-pre-line p-list"
>
4605 TVC VUMC
4605 Tvc Vumc
1301 Medical Center Drive
NASHVILLE, TN
Nashville, TN
37232-5310, USA
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe("Evaluate Ecr Metadata", () => {
},
{
title: "Custodian Address",
value: "3401 West End Ave\nNASHVILLE, TN\n37203, USA",
value: "3401 West End Ave\nNashville, TN\n37203, USA",
},
{
title: "Custodian Contact",
Expand Down Expand Up @@ -123,7 +123,7 @@ describe("Evaluate Ecr Metadata", () => {
},
{
title: "Author Facility Address",
value: ["3401 West End Ave\nNASHVILLE, TN\n37203, USA"],
value: ["3401 West End Ave\nNashville, TN\n37203, USA"],
},
{
title: "Author Facility Contact",
Expand Down Expand Up @@ -158,7 +158,7 @@ describe("Evaluate Ecr Metadata", () => {
},
{
title: "Author Facility Address",
value: ["3401 West End Ave\nNASHVILLE, TN\n37203, USA"],
value: ["3401 West End Ave\nNashville, TN\n37203, USA"],
},
{
title: "Author Facility Contact",
Expand All @@ -182,7 +182,7 @@ describe("Evaluate Ecr Metadata", () => {
},
{
title: "Author Address",
value: ["3401 West End Ave\nNASHVILLE, TN\n37203, USA"],
value: ["3401 West End Ave\nNashville, TN\n37203, USA"],
},
{
title: "Author Contact",
Expand All @@ -194,7 +194,7 @@ describe("Evaluate Ecr Metadata", () => {
},
{
title: "Author Facility Address",
value: ["3401 West End Ave\nNASHVILLE, TN\n37203, USA"],
value: ["3401 West End Ave\nNashville, TN\n37203, USA"],
},
{
title: "Author Facility Contact",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ describe("Evaluate Emergency Contact", () => {
mappings,
);
expect(actual).toEqual(
`sister\nAnastasia Bubbletea Pizza\n999 Single Court\nBEVERLY HILLS, CA\n90210, USA\nHome: 615-995-9999`,
`Sister\nAnastasia Bubbletea Pizza\n999 Single Court\nBeverly Hills, CA\n90210, USA\nHome: 615-995-9999`,
);
});
it("should return multiple emergency contacts", () => {
Expand Down Expand Up @@ -396,7 +396,7 @@ describe("Evaluate Emergency Contact", () => {
mappings,
);
expect(actual).toEqual(
`sister\nAnastasia Bubbletea Pizza\n999 Single Court\nBEVERLY HILLS, CA\n90210, USA\nHome: 615-995-9999\n\nbrother\nAlberto Bonanza Bartholomew Eggbert\nHome: 615-995-1000\nHome Fax: 615-995-1001`,
`Sister\nAnastasia Bubbletea Pizza\n999 Single Court\nBeverly Hills, CA\n90210, USA\nHome: 615-995-9999\n\nBrother\nAlberto Bonanza Bartholomew Eggbert\nHome: 615-995-1000\nHome Fax: 615-995-1001`,
);
});
it("should not return empty space when address is not available in", () => {
Expand Down Expand Up @@ -438,7 +438,7 @@ describe("Evaluate Emergency Contact", () => {
mappings,
);
expect(actual).toEqual(
`sister\nAnastasia Bubbletea Pizza\nHome: 615-995-9999`,
`Sister\nAnastasia Bubbletea Pizza\nHome: 615-995-9999`,
);
});
it("should return undefined if a patient has no contact", () => {
Expand All @@ -456,7 +456,7 @@ describe("Evaluate Patient Address", () => {
BundleWithPatient as unknown as Bundle,
mappings,
);
expect(actual).toEqual("1050 CARPENTER ST\nEDWARDS, CA\n93523-2800, US");
expect(actual).toEqual("1050 Carpenter St\nEdwards, CA\n93523-2800, US");
});
it("should return all 3 of the addresses", () => {
const actual = evaluatePatientAddress(
Expand All @@ -465,8 +465,8 @@ describe("Evaluate Patient Address", () => {
);
expect(actual).toEqual(
"Home:\n" +
"1050 CARPENTER ST\n" +
"EDWARDS, CA\n" +
"1050 Carpenter St\n" +
"Edwards, CA\n" +
"93523-2800, US\n" +
"\n" +
"Vacation:\n" +
Expand All @@ -489,7 +489,7 @@ describe("Evaluate Patient Name", () => {
mappings,
false,
);
expect(actual).toEqual("ABEL CASTILLO");
expect(actual).toEqual("Abel Castillo");
});
it("should return all 2 of the names", () => {
const actual = evaluatePatientName(
Expand All @@ -498,7 +498,7 @@ describe("Evaluate Patient Name", () => {
false,
);
expect(actual).toEqual(
"Official: ABEL CASTILLO\n" + "Nickname: Billy The Kid",
"Official: Abel Castillo\n" + "Nickname: Billy The Kid",
);
});
it("should only return the official name for the banner", () => {
Expand All @@ -507,15 +507,15 @@ describe("Evaluate Patient Name", () => {
mappings,
true,
);
expect(actual).toEqual("ABEL CASTILLO");
expect(actual).toEqual("Abel Castillo");
});
it("should only return the official name for the banner", () => {
const actual = evaluatePatientName(
BundleWithPatient as unknown as Bundle,
mappings,
true,
);
expect(actual).toEqual("ABEL CASTILLO");
expect(actual).toEqual("Abel Castillo");
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getFirstNonCommentChild,
formatAddress,
formatPhoneNumber,
toTitleCase,
} from "@/app/services/formatService";
import { ContactPoint, HumanName } from "fhir/r4";

Expand Down Expand Up @@ -619,6 +620,34 @@ describe("toSentenceCase", () => {
});
});

describe("toTitleCase", () => {
it("should return string in title case", () => {
const input = "ABEL CASTILLO";
const expected = "Abel Castillo";

const result = toTitleCase(input);
expect(result).toEqual(expected);
});
it("should return single-character word in title case", () => {
const input = "a";
const expected = "A";

const result = toTitleCase(input);
expect(result).toEqual(expected);
});
it("should return multi-line string in title case", () => {
const input = "facility name 1\nfacility name 2";
const expected = "Facility Name 1\nFacility Name 2";

const result = toTitleCase(input);
expect(result).toEqual(expected);
});
it("should return undefined if string is empty", () => {
const result = toTitleCase(undefined);
expect(result).toBeUndefined();
});
});

describe("removeHtmlElements", () => {
it("should remove all HTML tags from string", () => {
const input = "<div><p>Hello <br/>there</p></div>";
Expand Down Expand Up @@ -896,13 +925,13 @@ describe("getFirstNonCommentChild", () => {
describe("Format address", () => {
it("should format a full address", () => {
const actual = formatAddress({
line: ["123 Main street", "Unit 2"],
city: "City",
line: ["123 maIn stREet", "unit 2"],
city: "city",
state: "ST",
postalCode: "00000",
country: "USA",
});
expect(actual).toEqual("123 Main street\nUnit 2\nCity, ST\n00000, USA");
expect(actual).toEqual("123 Main Street\nUnit 2\nCity, ST\n00000, USA");
});
it("should skip undefined values", () => {
const actual = formatAddress({
Expand All @@ -911,7 +940,7 @@ describe("Format address", () => {
postalCode: "00000",
country: "USA",
});
expect(actual).toEqual("123 Main street\nUnit 2\nST\n00000, USA");
expect(actual).toEqual("123 Main Street\nUnit 2\nST\n00000, USA");
});

it("should return empty string if no values are available", () => {
Expand Down Expand Up @@ -941,7 +970,7 @@ describe("Format address", () => {
{ includeUse: true },
);
expect(actual).toEqual(
"Home:\n123 Main street\nUnit 2\nCity, ST\n00000, USA",
"Home:\n123 Main Street\nUnit 2\nCity, ST\n00000, USA",
);
});

Expand All @@ -959,7 +988,7 @@ describe("Format address", () => {
{ includePeriod: true },
);
expect(actual).toEqual(
"123 Main street\nUnit 2\nCity, ST\n00000, USA\nDates: 03/13/2024 - 04/14/2024",
"123 Main Street\nUnit 2\nCity, ST\n00000, USA\nDates: 03/13/2024 - 04/14/2024",
);
});

Expand All @@ -977,7 +1006,7 @@ describe("Format address", () => {
{ includePeriod: true },
);
expect(actual).toEqual(
"123 Main street\nUnit 2\nCity, ST\n00000, USA\nDates: 03/13/2024 - Present",
"123 Main Street\nUnit 2\nCity, ST\n00000, USA\nDates: 03/13/2024 - Present",
);
});

Expand All @@ -995,7 +1024,7 @@ describe("Format address", () => {
{ includePeriod: true },
);
expect(actual).toEqual(
"123 Main street\nUnit 2\nCity, ST\n00000, USA\nDates: Unknown - 03/13/2024",
"123 Main Street\nUnit 2\nCity, ST\n00000, USA\nDates: Unknown - 03/13/2024",
);
});

Expand All @@ -1009,7 +1038,7 @@ describe("Format address", () => {
use: "home",
period: { start: "03/13/2024" },
});
expect(actual).toEqual("123 Main street\nUnit 2\nCity, ST\n00000, USA");
expect(actual).toEqual("123 Main Street\nUnit 2\nCity, ST\n00000, USA");
});
});

Expand Down
5 changes: 3 additions & 2 deletions containers/ecr-viewer/src/app/tests/utils.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,9 @@ describe("Utils", () => {
const actual = evaluatePatientName(
BundleWithPatient as unknown as Bundle,
mappings,
false,
);
expect(actual).toEqual("ABEL CASTILLO");
expect(actual).toEqual("Abel Castillo");
});
});
describe("Extract Patient Address", () => {
Expand All @@ -183,7 +184,7 @@ describe("Utils", () => {
mappings,
);

expect(actual).toEqual("1050 CARPENTER ST\nEDWARDS, CA\n93523-2800, US");
expect(actual).toEqual("1050 Carpenter St\nEdwards, CA\n93523-2800, US");
});
});
describe("Calculate Patient Age", () => {
Expand Down

0 comments on commit 49222c5

Please sign in to comment.