Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add periods to patient addresses #3041

Merged
merged 14 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion containers/ecr-viewer/seed-scripts/create-seed-data.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def _process_files(args):
:return: A list of fhir bundles
"""
print("Converting files...")
subfolders = ["LA"]
subfolders = ["TN"]
mcmcgrath13 marked this conversation as resolved.
Show resolved Hide resolved

# Holds all of the rquests we are going to make
requests = []
Expand Down
6 changes: 1 addition & 5 deletions containers/ecr-viewer/src/app/api/fhirPath.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,12 @@ encounterStartDate: "Bundle.entry.resource.where(resourceType = 'Encounter').per
encounterDiagnosis: "Bundle.entry.resource.where(resourceType = 'Encounter').diagnosis"
encounterType: "Bundle.entry.resource.where(resourceType='Encounter')[0].class.display"
encounterID: "Bundle.entry.resource.where(resourceType='Encounter')[0].identifier"
facilityCity: "Bundle.entry.resource.where(resourceType = 'Location')[0].address.city"
facilityContact: "Bundle.entry.resource.where(resourceType = 'Location')[0].telecom.where(system = 'phone')[0].value"
facilityContactAddress: "Bundle.entry.resource.where(resourceType = 'Encounter')[0].serviceProvider"
facilityCountry: "Bundle.entry.resource.where(resourceType = 'Location')[0].address.country"
facilityLocation: "Bundle.entry.resource.where(resourceType = 'Encounter')[0].location[0].location.reference"
facilityName: "Bundle.entry.resource.where(resourceType = 'Encounter')[0].location[0].location.display"
facilityState: "Bundle.entry.resource.where(resourceType = 'Location')[0].address.state"
facilityStreetAddress: "Bundle.entry.resource.where(resourceType = 'Location')[0].address.line[0]"
facilityAddress: "Bundle.entry.resource.where(resourceType = 'Location')[0].address"
facilityType: "Bundle.entry.resource.where(resourceType = 'Encounter')[0].location[0].extension.where(url = 'http://build.fhir.org/ig/HL7/case-reporting/StructureDefinition-us-ph-location-definitions.html#Location.type').valueCodeableConcept.coding[0].display"
facilityZipCode: "Bundle.entry.resource.where(resourceType = 'Location')[0].address.postalCode"

compositionEncounterRef: "Bundle.entry.resource.where(resourceType = 'Composition').encounter.reference"
encounterIndividualRef: "Encounter.participant.where(type.coding.code = 'ATND').individual.reference"
Expand Down
24 changes: 3 additions & 21 deletions containers/ecr-viewer/src/app/services/ecrMetadataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,7 @@ export const evaluateEcrMetadata = (
},
{
title: "Custodian Address",
value: formatAddress(
custodian?.address?.[0].line,
custodian?.address?.[0].city,
custodian?.address?.[0].state,
custodian?.address?.[0].postalCode,
custodian?.address?.[0].country,
),
value: formatAddress(custodian?.address?.[0]),
},
{
title: "Custodian Contact",
Expand Down Expand Up @@ -238,13 +232,7 @@ const evaluateEcrAuthorDetails = (
{
title: "Author Address",
value: practitioner?.address?.map((address) =>
formatAddress(
address.line,
address.city,
address.state,
address.postalCode,
address.country,
),
formatAddress(address),
),
},
{
Expand All @@ -258,13 +246,7 @@ const evaluateEcrAuthorDetails = (
{
title: "Author Facility Address",
value: organization?.address?.map((address) =>
formatAddress(
address.line,
address.city,
address.state,
address.postalCode,
address.country,
),
formatAddress(address),
),
},
{
Expand Down
36 changes: 33 additions & 3 deletions containers/ecr-viewer/src/app/services/ecrSummaryService.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Bundle, Condition, Extension, Observation } from "fhir/r4";
import { Address, Bundle, Condition, Extension, Observation } from "fhir/r4";
import { evaluateData, PathMappings } from "@/app/view-data/utils/utils";
import {
formatAddress,
formatContactPoint,
formatDate,
formatStartEndDateTime,
} from "@/app/services/formatService";
import { evaluate } from "@/app/view-data/utils/evaluate";
import {
evaluatePatientName,
evaluatePatientAddress,
evaluateEncounterDiagnosis,
} from "./evaluateFhirDataService";
import { DisplayDataProps } from "@/app/view-data/components/DataDisplay";
Expand Down Expand Up @@ -72,7 +72,9 @@ export const evaluateEcrSummaryPatientDetails = (
},
{
title: "Patient Address",
value: evaluatePatientAddress(fhirBundle, fhirPathMappings),
value: findCurrentAddress(
evaluate(fhirBundle, fhirPathMappings.patientAddressList),
),
},
{
title: "Patient Contact",
Expand All @@ -83,6 +85,34 @@ export const evaluateEcrSummaryPatientDetails = (
]);
};

/**
* Find the most current home address.
* @param addresses - List of addresses.
* @returns A string with the formatted current address or an empty string if no address.
*/
export const findCurrentAddress = (addresses: Address[]) => {
// current home address is first pick
let address = addresses.find(
(a) => a.use === "home" && !!a.period?.start && !a.period?.end,
);
// then current address
if (!address) {
address = addresses.find((a) => !!a.period?.start && !a.period?.end);
}

// then home address
if (!address) {
address = addresses.find((a) => a.use == "home");
}

// then first address
if (!address) {
address = addresses[0];
}

return formatAddress(address);
};

/**
* Evaluates and retrieves encounter details from the FHIR bundle using the provided path mappings.
* @param fhirBundle - The FHIR bundle containing patient data.
Expand Down
54 changes: 9 additions & 45 deletions containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts
mcmcgrath13 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,10 @@ export const evaluatePatientAddress = (
if (addresses.length > 0) {
return addresses
.map((address) => {
return formatAddress(
address.line,
address.city,
address.state,
address.postalCode,
address.country,
addresses.length > 1 ? address?.use : undefined,
);
return formatAddress(address, {
includeUse: addresses.length > 1,
includePeriod: true,
});
})
.join("\n\n");
} else {
Expand Down Expand Up @@ -414,22 +410,12 @@ export const evaluateFacilityData = (
{
title: "Facility Address",
value: formatAddress(
evaluate(fhirBundle, mappings["facilityStreetAddress"]),
evaluate(fhirBundle, mappings["facilityCity"])[0],
evaluate(fhirBundle, mappings["facilityState"])[0],
evaluate(fhirBundle, mappings["facilityZipCode"])[0],
evaluate(fhirBundle, mappings["facilityCountry"])[0],
evaluate(fhirBundle, mappings["facilityAddress"])[0],
),
},
{
title: "Facility Contact Address",
value: formatAddress(
facilityContactAddress?.line,
facilityContactAddress?.city,
facilityContactAddress?.state,
facilityContactAddress?.postalCode,
facilityContactAddress?.country,
),
value: formatAddress(facilityContactAddress),
},
{
title: "Facility Contact",
Expand Down Expand Up @@ -489,15 +475,7 @@ export const evaluateProviderData = (
},
{
title: "Provider Address",
value: practitioner?.address?.map((address) =>
formatAddress(
address.line,
address.city,
address.state,
address.postalCode,
address.country,
),
),
value: practitioner?.address?.map((address) => formatAddress(address)),
},
{
title: "Provider Contact",
Expand All @@ -509,15 +487,7 @@ export const evaluateProviderData = (
},
{
title: "Provider Facility Address",
value: organization?.address?.map((address) =>
formatAddress(
address.line,
address.city,
address.state,
address.postalCode,
address.country,
),
),
value: organization?.address?.map((address) => formatAddress(address)),
},
{
title: "Provider ID",
Expand Down Expand Up @@ -603,13 +573,7 @@ export const evaluateEmergencyContact = (
.map((contact) => {
const relationship = contact.relationship?.[0].coding?.[0]?.display;

const address = formatAddress(
contact.address?.line,
contact.address?.city,
contact.address?.state,
contact.address?.postalCode,
contact.address?.country,
);
const address = formatAddress(contact.address);

const phoneNumbers = formatContactPoint(contact.telecom);

Expand Down
54 changes: 32 additions & 22 deletions containers/ecr-viewer/src/app/services/formatService.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import { ToolTipElement } from "@/app/view-data/components/ToolTipElement";
import { ContactPoint } from "fhir/r4";
import { Address, ContactPoint } from "fhir/r4";
import { RenderableNode, safeParse } from "../view-data/utils/utils";
import sanitizeHtml from "sanitize-html";

Expand Down Expand Up @@ -57,36 +57,46 @@ export const formatName = (
return nameArray.join(" ").trim();
};

const DEFAULT_ADDRESS_CONFIG = { includeUse: false, includePeriod: false };
type AddressConfig = { includeUse?: boolean; includePeriod?: boolean };

/**
* Formats an address based on its components.
* @param streetAddress - An array containing street address lines.
* @param city - The city name.
* @param state - The state or region name.
* @param zipCode - The ZIP code or postal code.
* @param country - The country name.
* @param [use] - Optional address use.
* @param address - Object with address parts
* @param address.line - An array containing street address lines.
* @param address.city - The city name.
* @param address.state - The state or region name.
* @param address.postalCode - The ZIP code or postal code.
* @param address.country - The country name.
* @param address.use - Optional address use.
* @param address.period - Optional address use.
* @param config - Configuration object to customize formatting
* @param config.includeUse - Include the use (e.g. `Home:`) on the address if available (default: false)
* @param config.includePeriod - Include the perios (e.g. `Dates: 12/11/2023 - Present`) on the address if available (default: false)
* @returns The formatted address string.
*/
export const formatAddress = (
streetAddress: string[] | undefined,
city: string | undefined,
state: string | undefined,
zipCode: string | undefined,
country: string | undefined,
use?: string | undefined,
{ line, city, state, postalCode, country, use, period }: Address = {},
config: AddressConfig = {},
) => {
let address = {
use: use || "",
streetAddress: streetAddress || [],
cityState: [city, state],
zipCodeCountry: [zipCode, country],
const { includeUse, includePeriod } = {
...DEFAULT_ADDRESS_CONFIG,
...config,
};

const formatDateLine = () => {
const stDt = formatDate(period?.start);
const endDt = formatDate(period?.end);
if (!stDt && !endDt) return false;
return `Dates: ${stDt ?? "Unknown"} - ${endDt ?? "Present"}`;
};

return [
address.use ? toSentenceCase(address.use) + ":" : "",
address.streetAddress.filter(Boolean).join("\n"),
address.cityState.filter(Boolean).join(", "),
address.zipCodeCountry.filter(Boolean).join(", "),
includeUse && use && toSentenceCase(use) + ":",
(line || []).filter(Boolean).join("\n"),
[city, state].filter(Boolean).join(", "),
[postalCode, country].filter(Boolean).join(", "),
includePeriod && formatDateLine(),
]
.filter(Boolean)
.join("\n");
Expand Down
8 changes: 1 addition & 7 deletions containers/ecr-viewer/src/app/services/labsService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -560,13 +560,7 @@ export const evaluateLabOrganizationData = (
matchingOrg = findIdenticalOrg(orgMappings, matchingOrg);
}
const orgAddress = matchingOrg?.address?.[0];
const formattedAddress = formatAddress(
orgAddress?.line,
orgAddress?.city,
orgAddress?.state,
orgAddress?.postalCode,
orgAddress?.country,
);
const formattedAddress = formatAddress(orgAddress);

const contactInfo = formatPhoneNumber(matchingOrg?.telecom?.[0].value);
const name = matchingOrg?.name ?? "";
Expand Down
54 changes: 54 additions & 0 deletions containers/ecr-viewer/src/app/tests/components/EcrSummary.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { axe } from "jest-axe";
import EcrSummary, {
ConditionSummary,
} from "../../view-data/components/EcrSummary";
import { findCurrentAddress } from "@/app/services/ecrSummaryService";

describe("EcrSummary", () => {
const patientDetails = [
Expand Down Expand Up @@ -186,3 +187,56 @@ describe("EcrSummary", () => {
expect(screen.getByText("2 CONDITIONS FOUND"));
});
});

describe("findCurrentAddress", () => {
const base = {
line: ["123 Main St"],
};

it("should return empty when no addresses available", () => {
const actual = findCurrentAddress([]);

expect(actual).toEqual("");
});

it("should return first address when no use or period", () => {
const actual = findCurrentAddress([
{ ...base, city: "1" },
{ ...base, city: "2" },
]);

expect(actual).toEqual("123 Main St\n1");
});

it("should return first home address when no current period", () => {
const actual = findCurrentAddress([
{ ...base, use: "work", city: "1" },
{ ...base, use: "home", city: "2" },
{ ...base, use: "home", city: "3" },
{
...base,
use: "home",
city: "3",
period: { start: "2020-03-01", end: "2020-04-01" },
},
]);

expect(actual).toEqual("123 Main St\n2");
});

it("should return current home address", () => {
const actual = findCurrentAddress([
{ ...base, use: "work", city: "1" },
{ ...base, use: "home", city: "2" },
{ ...base, use: "home", city: "3", period: { start: "2024-03-13" } },
{
...base,
use: "home",
city: "4",
period: { start: "2024-03-10", end: "2024-03-12" },
},
]);

expect(actual).toEqual("123 Main St\n3");
});
});
Loading
Loading