Skip to content

feat: Public Health Emergency Outbreak Info field #680

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

Merged
merged 21 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
cff1474
Add outbreak info
JNygaard-Skylight May 1, 2025
52263cf
Add outbreak info
JNygaard-Skylight May 1, 2025
6d496d3
Merge branch 'josh/592/public-health-emergency' of github.com-jnygaar…
JNygaard-Skylight May 2, 2025
203b3d8
format outbreak info
JNygaard-Skylight May 2, 2025
50b80ae
handle effective period
JNygaard-Skylight May 2, 2025
2d93b67
outbreak info test
JNygaard-Skylight May 2, 2025
9968479
fix: There should be a clinical info section if just outbreak info
JNygaard-Skylight May 5, 2025
598a2ac
reduce multiple outbreak info into single string
JNygaard-Skylight May 5, 2025
7dc2fe2
Add outbreak info
JNygaard-Skylight May 1, 2025
13fd78d
format outbreak info
JNygaard-Skylight May 2, 2025
3b7baf7
handle effective period
JNygaard-Skylight May 2, 2025
0d5afbe
outbreak info test
JNygaard-Skylight May 2, 2025
c5eca07
fix: There should be a clinical info section if just outbreak info
JNygaard-Skylight May 5, 2025
0f7281d
reduce multiple outbreak info into single string
JNygaard-Skylight May 5, 2025
c0adeec
Merge branch 'josh/592/public-health-emergency' of github.com-jnygaar…
JNygaard-Skylight May 5, 2025
67fb0ff
Merge branch 'main' into josh/592/public-health-emergency
JNygaard-Skylight May 7, 2025
bd1d4df
refactor: use map, join instead of reduce
JNygaard-Skylight May 7, 2025
a0e14aa
refactor: use find instead of for/break
JNygaard-Skylight May 7, 2025
3b4a3d2
Merge branch 'josh/592/public-health-emergency' of github.com-jnygaar…
JNygaard-Skylight May 7, 2025
071dddd
update FHIR converter version
JNygaard-Skylight Jun 3, 2025
19df9d7
Merge branch 'main' into josh/592/public-health-emergency
JNygaard-Skylight Jun 3, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ describe("Snapshot test for Procedures (Treatment Details)", () => {
<ClinicalInfo
clinicalNotes={[]}
activeProblemsDetails={[]}
emergencyOutbreakInfo={[]}
vitalData={[]}
reasonForVisitDetails={[]}
immunizationsDetails={[]}
Expand Down Expand Up @@ -145,6 +146,7 @@ describe("Snapshot test for Clinical Notes", () => {
<ClinicalInfo
clinicalNotes={clinicalNotes}
activeProblemsDetails={[]}
emergencyOutbreakInfo={[]}
vitalData={[]}
reasonForVisitDetails={[]}
immunizationsDetails={[]}
Expand Down Expand Up @@ -212,6 +214,7 @@ describe("Snapshot test for Clinical Notes", () => {
<ClinicalInfo
clinicalNotes={clinicalNotes}
activeProblemsDetails={[]}
emergencyOutbreakInfo={[]}
vitalData={[]}
reasonForVisitDetails={[]}
immunizationsDetails={[]}
Expand All @@ -235,13 +238,15 @@ describe("Check that Clinical Info components render given FHIR bundle", () => {
const testReasonForVisitData =
testClinicalData.reasonForVisitDetails.availableData;
const testTreatmentData = testClinicalData.treatmentData.availableData;
const testOutbreakInfo = testClinicalData.emergencyOutbreakInfo.availableData;

it("eCR Viewer renders immunization table given FHIR bundle with immunization info", () => {
const clinicalInfo = render(
<ClinicalInfo
immunizationsDetails={testImmunizationsData}
reasonForVisitDetails={[]}
activeProblemsDetails={[]}
emergencyOutbreakInfo={[]}
vitalData={[]}
treatmentData={[]}
clinicalNotes={[]}
Expand All @@ -266,6 +271,7 @@ describe("Check that Clinical Info components render given FHIR bundle", () => {
immunizationsDetails={[]}
reasonForVisitDetails={[]}
activeProblemsDetails={testActiveProblemsData}
emergencyOutbreakInfo={[]}
vitalData={[]}
treatmentData={[]}
clinicalNotes={[]}
Expand All @@ -288,6 +294,7 @@ describe("Check that Clinical Info components render given FHIR bundle", () => {
immunizationsDetails={[]}
reasonForVisitDetails={[]}
activeProblemsDetails={[]}
emergencyOutbreakInfo={[]}
vitalData={testVitalSignsData}
treatmentData={[]}
clinicalNotes={[]}
Expand Down Expand Up @@ -326,6 +333,7 @@ describe("Check that Clinical Info components render given FHIR bundle", () => {
immunizationsDetails={[]}
reasonForVisitDetails={testReasonForVisitData}
activeProblemsDetails={[]}
emergencyOutbreakInfo={[]}
vitalData={[]}
treatmentData={[]}
clinicalNotes={[]}
Expand All @@ -343,6 +351,7 @@ describe("Check that Clinical Info components render given FHIR bundle", () => {
immunizationsDetails={[]}
reasonForVisitDetails={[]}
activeProblemsDetails={[]}
emergencyOutbreakInfo={[]}
vitalData={[]}
treatmentData={testTreatmentData}
clinicalNotes={[]}
Expand All @@ -358,12 +367,31 @@ describe("Check that Clinical Info components render given FHIR bundle", () => {
expect(expectedTable.length).toEqual(4);
});

it("eCR Viewer renders emergency outbreak info given FHIR bundle with emergency outbreak info", () => {
const clinicalInfo = render(
<ClinicalInfo
immunizationsDetails={[]}
reasonForVisitDetails={[]}
activeProblemsDetails={[]}
emergencyOutbreakInfo={testOutbreakInfo}
vitalData={[]}
treatmentData={[]}
clinicalNotes={[]}
/>,
);
const expectedEmergencyOutbreakElement = clinicalInfo.getByTestId(
"emergency-outbreak-info",
);
expect(expectedEmergencyOutbreakElement).toBeInTheDocument();
});

it("eCR Viewer renders all Clinical Info sections", () => {
const clinicalInfo = render(
<ClinicalInfo
immunizationsDetails={testImmunizationsData}
reasonForVisitDetails={testReasonForVisitData}
activeProblemsDetails={testActiveProblemsData}
emergencyOutbreakInfo={[]}
vitalData={testVitalSignsData}
treatmentData={testTreatmentData}
clinicalNotes={[]}
Expand Down
19 changes: 11 additions & 8 deletions containers/ecr-viewer/src/app/utils/evaluate/fhir-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export type PathTypes = {
clinicalReasonForVisit: ValueX;
patientVitalSigns: Observation;
vitalSignType: CodeableConcept;
vitalSignValue: ValueX;
value: ValueX;
vitalSignDateTime: ValueX;
resolve: unknown;
activeProblems: Condition;
Expand All @@ -109,6 +109,7 @@ export type PathTypes = {
activeProblemsOnsetAge: ValueX;
activeProblemsComments: string;
historyOfPresentIllness: string;
emergencyOutbreakInfo: Observation;
planOfTreatment: string;
plannedProcedures: CarePlanActivity;
plannedProcedureName: string;
Expand Down Expand Up @@ -412,12 +413,6 @@ const _fhirPathMappings: { [K in FhirPathKeys]: Omit<FhirPath<K>, "name"> } = {
path: "Bundle.entry.resource.where(meta.profile = 'http://hl7.org/fhir/us/ecr/StructureDefinition/rr-reportability-information-observation')",
},

// Clinical Data
clinicalReasonForVisit: {
type: "ValueX",
path: "Bundle.entry.resource.section.where(title.lower() = 'reason for visit')[0].extension[0].value",
},

// Vitals
patientVitalSigns: {
type: "Observation",
Expand All @@ -427,7 +422,7 @@ const _fhirPathMappings: { [K in FhirPathKeys]: Omit<FhirPath<K>, "name"> } = {
type: "CodeableConcept",
path: "code",
},
vitalSignValue: {
value: {
type: "ValueX",
path: "value",
},
Expand All @@ -442,6 +437,10 @@ const _fhirPathMappings: { [K in FhirPathKeys]: Omit<FhirPath<K>, "name"> } = {
},

// Clinical Info
clinicalReasonForVisit: {
type: "ValueX",
path: "Bundle.entry.resource.section.where(title.lower() = 'reason for visit')[0].extension[0].value",
},
activeProblems: {
type: "Condition",
path: "Bundle.entry.resource.where(resourceType = 'Condition').where(category.coding.code = 'problem-item-list')",
Expand All @@ -457,6 +456,10 @@ const _fhirPathMappings: { [K in FhirPathKeys]: Omit<FhirPath<K>, "name"> } = {
type: "string",
path: "Bundle.entry.resource.where(resourceType = 'Composition').section.where(code.coding.code = '10164-2').text.`div`.first()",
},
emergencyOutbreakInfo: {
type: "Observation",
path: "Bundle.entry.resource.where(resourceType = 'Observation').where(meta.profile = 'http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-emergency-outbreak-information')",
},

// Treatment Details
planOfTreatment: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DataDisplay, DataTableDisplay, DisplayDataProps } from "./DataDisplay";
interface ClinicalProps {
reasonForVisitDetails: DisplayDataProps[];
activeProblemsDetails: DisplayDataProps[];
emergencyOutbreakInfo: DisplayDataProps[];
vitalData: DisplayDataProps[];
immunizationsDetails: DisplayDataProps[];
treatmentData: DisplayDataProps[];
Expand Down Expand Up @@ -62,11 +63,17 @@ const ClinicalNotes = ({ details }: { details: DisplayDataProps[] }) => {
const SymptomsAndProblems = ({
symptoms,
problems,
emergencyOutbreakInfo,
}: {
symptoms: DisplayDataProps[];
problems: DisplayDataProps[];
emergencyOutbreakInfo: DisplayDataProps[];
}) => {
if (symptoms?.length > 0 || problems?.length > 0) {
if (
symptoms?.length > 0 ||
problems?.length > 0 ||
emergencyOutbreakInfo?.length > 0
) {
return (
<AccordionSubSection title="Symptoms and Problems">
<div data-testid="reason-for-visit">
Expand All @@ -80,6 +87,11 @@ const SymptomsAndProblems = ({
className="table-clinical-problems"
/>
</div>
<div data-testid="emergency-outbreak-info">
{emergencyOutbreakInfo.map((item, index) => (
<DataDisplay item={item} key={index} />
))}
</div>
</AccordionSubSection>
);
}
Expand Down Expand Up @@ -138,6 +150,7 @@ const VitalDetails = ({ details }: { details: DisplayDataProps[] }) => {
* @param props - Props containing clinical information.
* @param props.reasonForVisitDetails - The details of the reason for visit.
* @param props.activeProblemsDetails - The details of active problems.
* @param props.emergencyOutbreakInfo - The details of emergency outbreak information.
* @param props.immunizationsDetails - The details of immunizations.
* @param props.vitalData - The vital signs data.
* @param props.treatmentData - The details of treatments.
Expand All @@ -147,6 +160,7 @@ const VitalDetails = ({ details }: { details: DisplayDataProps[] }) => {
export const ClinicalInfo = ({
reasonForVisitDetails,
activeProblemsDetails,
emergencyOutbreakInfo,
immunizationsDetails,
vitalData,
treatmentData,
Expand All @@ -158,6 +172,7 @@ export const ClinicalInfo = ({
<SymptomsAndProblems
symptoms={reasonForVisitDetails}
problems={activeProblemsDetails}
emergencyOutbreakInfo={emergencyOutbreakInfo}
/>
<TreatmentDetails details={treatmentData} />
<ImmunizationsDetails details={immunizationsDetails} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const getEcrDocumentAccordionItems = (
hospitalEncounterData.unavailableData,
clinicalData.reasonForVisitDetails.unavailableData,
clinicalData.activeProblemsDetails.unavailableData,
clinicalData.emergencyOutbreakInfo.unavailableData,
providerData.unavailableData,
clinicalData.vitalData.unavailableData,
clinicalData.immunizationsDetails.unavailableData,
Expand Down Expand Up @@ -125,6 +126,9 @@ export const getEcrDocumentAccordionItems = (
activeProblemsDetails={
clinicalData.activeProblemsDetails.availableData
}
emergencyOutbreakInfo={
clinicalData.emergencyOutbreakInfo.availableData
}
vitalData={clinicalData.vitalData.availableData}
immunizationsDetails={clinicalData.immunizationsDetails.availableData}
treatmentData={clinicalData.treatmentData.availableData}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ export const evaluateClinicalData = (fhirBundle: Bundle) => {
},
];

const emergencyOutbreakInfo: DisplayDataProps[] = [
{
title: "Emergency Outbreak Info",
value: evaluateOutbreakInfo(fhirBundle),
},
];

const administeredMedication = evaluateAdministeredMedication(fhirBundle);

const treatmentData: DisplayDataProps[] = [
Expand Down Expand Up @@ -122,6 +129,7 @@ export const evaluateClinicalData = (fhirBundle: Bundle) => {
clinicalNotes: evaluateData(clinicalNotes),
reasonForVisitDetails: evaluateData(reasonForVisitData),
activeProblemsDetails: evaluateData(activeProblemsTableData),
emergencyOutbreakInfo: evaluateData(emergencyOutbreakInfo),
treatmentData: evaluateData(treatmentData),
vitalData: evaluateData(vitalData),
immunizationsDetails: evaluateData(immunizationsData),
Expand Down Expand Up @@ -420,7 +428,7 @@ export const returnVitalsTable = (fhirBundle: Bundle) => {
infoPath: "vitalSignType",
applyToValue: toSentenceCase,
},
{ columnName: "Result", infoPath: "vitalSignValue" },
{ columnName: "Result", infoPath: "value" },
{
columnName: "Date/Time",
infoPath: "vitalSignDateTime",
Expand All @@ -438,3 +446,37 @@ export const returnVitalsTable = (fhirBundle: Bundle) => {
/>
);
};

const evaluateOutbreakInfo = (fhirBundle: Bundle): string => {
const outbreakInfos = evaluateAll(
fhirBundle,
fhirPathMappings.emergencyOutbreakInfo,
);

return outbreakInfos
.map((outbreakInfo) => {
const lines = [];

if (outbreakInfo.effectiveDateTime) {
lines.push(
"Date/Time: " + formatDateTime(outbreakInfo.effectiveDateTime),
);
} else if (outbreakInfo.effectivePeriod) {
lines.push(formatStartEndDate(outbreakInfo.effectivePeriod));
}

// Get the first display value from the coding array
const coding = outbreakInfo.code?.coding?.find((c) => c.display);
if (coding?.display) {
lines.push("Type: " + coding.display);
}

const value = evaluateValue(outbreakInfo, fhirPathMappings.value);
if (value) {
lines.push("Result: " + value);
}

return lines.join("\n");
})
.join("\n\n");
};
2 changes: 1 addition & 1 deletion containers/fhir-converter/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build

# Download FHIR-Converter
RUN git clone https://github.com/CDCgov/dibbs-FHIR-Converter.git --branch v7.0-4 --depth 1 /build/FHIR-Converter
RUN git clone https://github.com/CDCgov/dibbs-FHIR-Converter.git --branch v7.0-5 --depth 1 /build/FHIR-Converter

WORKDIR /build/FHIR-Converter

Expand Down
41 changes: 41 additions & 0 deletions test-data/fhir/BundleClinicalInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,47 @@
"method": "PUT",
"url": "MedicationAdministration/367be6b3-75f2-2053-63e4-f0a6f1d6bff1"
}
},
{
"fullUrl": "urn:uuid:26d05bcc-1b0f-fc8a-a85e-4c6c6512478f",
"resource": {
"resourceType": "Observation",
"id": "26d05bcc-1b0f-fc8a-a85e-4c6c6512478f",
"meta": {
"profile": [
"http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-emergency-outbreak-information"
],
"source": "ecr"
},
"identifier": [
{
"system": "urn:ietf:rfc:3986",
"value": "urn:uuid:ab1791b0-5c71-11db-b0de-0800200c9a54"
}
],
"status": "final",
"code": {
"coding": [
{
"display": "Employee desk distance from mail sorter"
}
]
},
"subject": {
"reference": "Patient/f35db4f9-0d60-4d3d-ac02-75affb3f2059"
},
"valueQuantity": {
"value": 2,
"unit": "m"
},
"effectivePeriod": {
"start": "2020-11-01"
}
},
"request": {
"method": "PUT",
"url": "Observation/26d05bcc-1b0f-fc8a-a85e-4c6c6512478f"
}
}
]
}