diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..3e8830f14e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# they will be requested for review when someone opens a pull request. +* @akasper @angelathe @austin-hall-skylight @BobanL @gordonfarrell @JNygaard-Skylight @lina-roth @mcmcgrath13 @robertmitchellv diff --git a/containers/ecr-viewer/src/app/api/fhirPath.yml b/containers/ecr-viewer/src/app/api/fhirPath.yml index 46aeb97a91..6361e79e1d 100644 --- a/containers/ecr-viewer/src/app/api/fhirPath.yml +++ b/containers/ecr-viewer/src/app/api/fhirPath.yml @@ -2,6 +2,7 @@ patientNameList: "Bundle.entry.resource.where(resourceType = 'Patient').name" patientAddressList: "Bundle.entry.resource.where(resourceType = 'Patient').address" patientTelecom: "Bundle.entry.resource.where(resourceType = 'Patient').telecom" patientCounty: "Bundle.entry.resource.where(resourceType = 'Patient').address.first().county" +patientCountry: "Bundle.entry.resource.where(resourceType = 'Patient').address.first().country" patientIds: "Bundle.entry.resource.where(resourceType = 'Patient').identifier.where(system != 'urn:ietf:rfc:3986').value.join('\n')" patientDOB: "Bundle.entry.resource.where(resourceType = 'Patient').birthDate" @@ -21,7 +22,9 @@ patientCurrentJobTitle: "Bundle.entry.resource.where(resourceType='Observation') patientTobaccoUse: "Bundle.entry.resource.where(resourceType='Observation').where(code.coding.code='72166-2').where(category.coding.code='social-history').value" patientHomelessStatus: "Bundle.entry.resource.where(resourceType='Observation').where(code.coding.code='75274-1').where(category.coding.code='social-history').value" patientPregnancyStatus: "Bundle.entry.resource.where(resourceType='Observation').where(meta.profile='http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-pregnancy-status-observation').value" -patientAlcoholUse: "Bundle.entry.resource.where(resourceType='Observation').where(code.coding.code='11331-6').where(category.coding.code='social-history').value" +patientAlcoholUse: "Bundle.entry.resource.where(resourceType='Observation').where(code.coding.where(code='11331-6' and system = 'http://loinc.org')).value" +patientAlcoholIntake: "Bundle.entry.resource.where(resourceType='Observation').where(code.coding.where(code='74013-4' and system = 'http://loinc.org')).value" +patientAlcoholComment: "Bundle.entry.resource.where(resourceType='Observation').where(code.coding.display='Alcohol Comment').value" patientSexualOrientation: "Bundle.entry.resource.where(resourceType='Observation').where(code.coding.code='76690-7').value" patientGenderIdentity: "Bundle.entry.resource.where(resourceType = 'Patient').extension.where(url='http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-genderidentity-extension').value" patientReligion: "Bundle.entry.resource.where(resourceType = 'Patient').extension.where(url='http://hl7.org/fhir/StructureDefinition/patient-religion').value" diff --git a/containers/ecr-viewer/src/app/api/save-fhir-data/save-fhir-data-service.ts b/containers/ecr-viewer/src/app/api/save-fhir-data/save-fhir-data-service.ts index bf9d37d237..82ec31d434 100644 --- a/containers/ecr-viewer/src/app/api/save-fhir-data/save-fhir-data-service.ts +++ b/containers/ecr-viewer/src/app/api/save-fhir-data/save-fhir-data-service.ts @@ -358,12 +358,20 @@ export const saveMetadataToSqlServer = async ( sql.VarChar(50), lab.test_result_code_system, ) - .input("test_result_interpretation", sql.VarChar(255), null) // Not implemented - .input("test_result_interpretation_code", sql.VarChar(50), null) // Not implemented + .input( + "test_result_interpretation", + sql.VarChar(255), + lab.test_result_interpretation, + ) + .input( + "test_result_interpretation_code", + sql.VarChar(50), + lab.test_result_interpretation_code, + ) .input( "test_result_interpretation_system", sql.VarChar(255), - lab.test_result_interp_system, + lab.test_result_interpretation_system, ) .input( "test_result_ref_range_low_value", diff --git a/containers/ecr-viewer/src/app/api/save-fhir-data/types.ts b/containers/ecr-viewer/src/app/api/save-fhir-data/types.ts index f363ddb490..fa4aafc5b7 100644 --- a/containers/ecr-viewer/src/app/api/save-fhir-data/types.ts +++ b/containers/ecr-viewer/src/app/api/save-fhir-data/types.ts @@ -9,7 +9,9 @@ interface Lab { test_result_code: string | undefined; test_result_code_display: string | undefined; test_result_code_system: string | undefined; - test_result_interp_system: string | undefined; + test_result_interpretation: string | undefined; + test_result_interpretation_code: string | undefined; + test_result_interpretation_system: string | undefined; test_result_ref_range_low: string | undefined; test_result_ref_range_low_units: string | undefined; test_result_ref_range_high: string | undefined; diff --git a/containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts b/containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts index ca60b3d61a..646e4fe616 100644 --- a/containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts +++ b/containers/ecr-viewer/src/app/services/evaluateFhirDataService.ts @@ -31,6 +31,7 @@ import { DisplayDataProps } from "@/app/view-data/components/DataDisplay"; import { evaluateTravelHistoryTable } from "./socialHistoryService"; import { Path } from "fhirpath"; import { returnTableFromJson } from "../view-data/components/common"; +import { toSentenceCase } from "./formatService"; /** * Evaluates patient name from the FHIR bundle and formats it into structured data for display. @@ -197,6 +198,39 @@ export const calculatePatientAgeAtDeath = ( } }; +/** + * Evaluates alcohol use information from the FHIR bundle and formats it into structured data for display. + * @param fhirBundle - The FHIR bundle containing alcohol use data. + * @param fhirMappings - The object containing the fhir paths. + * @returns An array of evaluated and formatted alcohol use data. + */ +export const evaluateAlcoholUse = ( + fhirBundle: Bundle, + fhirMappings: PathMappings, +) => { + const alcoholUse = evaluateValue(fhirBundle, fhirMappings.patientAlcoholUse); + const alcoholIntake = evaluateValue( + fhirBundle, + fhirMappings.patientAlcoholIntake, + ); + let alcoholComment: string | undefined = evaluateValue( + fhirBundle, + fhirMappings.patientAlcoholComment, + ); + + if (alcoholComment) { + alcoholComment = toSentenceCase(alcoholComment); + } + + return [ + alcoholUse ? `Use: ${alcoholUse}` : null, + alcoholIntake ? `Intake (standard drinks/week): ${alcoholIntake}` : null, + alcoholComment ? `Comment: ${alcoholComment}` : null, + ] + .filter(Boolean) // Removes null or undefined lines + .join("\n"); // Joins the remaining lines with newlines +}; + /** * Evaluates social data from the FHIR bundle and formats it into structured data for display. * @param fhirBundle - The FHIR bundle containing social data. @@ -227,7 +261,7 @@ export const evaluateSocialData = ( }, { title: "Alcohol Use", - value: evaluateValue(fhirBundle, mappings["patientAlcoholUse"]), + value: evaluateAlcoholUse(fhirBundle, mappings), }, { title: "Sexual Orientation", @@ -313,6 +347,10 @@ export const evaluateDemographicsData = ( title: "County", value: evaluate(fhirBundle, mappings.patientCounty)[0], }, + { + title: "Country", + value: evaluate(fhirBundle, mappings.patientCountry)[0], + }, { title: "Contact", value: formatContactPoint(evaluate(fhirBundle, mappings.patientTelecom)), diff --git a/containers/ecr-viewer/src/app/services/labsService.tsx b/containers/ecr-viewer/src/app/services/labsService.tsx index bb2e085fe3..8fac361771 100644 --- a/containers/ecr-viewer/src/app/services/labsService.tsx +++ b/containers/ecr-viewer/src/app/services/labsService.tsx @@ -332,7 +332,11 @@ export function evaluateObservationTable( ).filter( (observation) => !observation.component && - observation.code?.coding.some((c: Coding) => c?.display), + // Make sure there is a component name, but it isn't "Lab Interpretation" as that's handled + // via the tab on the result's name + observation.code?.coding.some( + (c: Coding) => c?.display && c?.display !== "Lab Interpretation", + ), ); let obsTable; diff --git a/containers/ecr-viewer/src/app/tests/assets/BundleLab.json b/containers/ecr-viewer/src/app/tests/assets/BundleLab.json index 5136dc422d..7be4f68629 100644 --- a/containers/ecr-viewer/src/app/tests/assets/BundleLab.json +++ b/containers/ecr-viewer/src/app/tests/assets/BundleLab.json @@ -83,6 +83,10 @@ { "display": "Organism ID", "reference": "Observation/2a4f8fb5-f957-3c30-1353-a30aa441a12b" + }, + { + "reference": "Observation/e9c8e4b3-3fec-4f29-a5c5-e45f96680a4c", + "display": "Lab Interpretation" } ] } @@ -265,6 +269,9 @@ { "reference": "Observation/1c0f3367-0588-c90e-fed0-0d8c15c5ac1b" }, + { + "reference": "Observation/e9c8e4b3-3fec-4f29-a5c5-e45f96680a4c" + }, { "reference": "Observation/ab1ecbd4-6de0-4f78-cea6-a880a15e88bb" } @@ -396,6 +403,73 @@ "url": "DiagnosticReport/b0f590a6-4bf5-7add-9716-2bd3ba6defb2" } }, + { + "fullUrl": "urn:uuid:e9c8e4b3-3fec-4f29-a5c5-e45f96680a4c", + "resource": { + "resourceType": "Observation", + "id": "e9c8e4b3-3fec-4f29-a5c5-e45f96680a4c", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observationresults" + ], + "source": "ecr" + }, + "identifier": [ + { + "system": "urn:oid:1.2.840.114350.1.13.202.3.7.2.798268", + "value": "476858944" + } + ], + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory" + } + ] + } + ], + "status": "final", + "code": { + "coding": [ + { + "code": "56850-1", + "system": "http://loinc.org", + "display": "Lab Interpretation" + } + ] + }, + "effectiveDateTime": "2022-09-29T21:13:00Z", + "valueString": "Abnormal", + "extension": [ + { + "url": "http://hl7.org/fhir/R4/specimen.html", + "extension": [ + { + "url": "specimen source", + "valueString": "Stool" + }, + { + "url": "specimen collection time", + "valueDateTime": "2022-09-28T20:51:00Z" + }, + { + "url": "specimen receive time", + "valueDateTime": "2022-09-28T20:51:36Z" + } + ] + } + ], + "subject": { + "reference": "Patient/1586f68b-1a17-4188-ac0b-82c3cbdbfbda" + } + }, + "request": { + "method": "PUT", + "url": "Observation/e9c8e4b3-3fec-4f29-a5c5-e45f96680a4c" + } + }, { "fullUrl": "urn:uuid:1c0f3367-0588-c90e-fed0-0d8c15c5ac1b", "resource": { diff --git a/containers/ecr-viewer/src/app/tests/assets/BundlePatient.json b/containers/ecr-viewer/src/app/tests/assets/BundlePatient.json index 1a49d29671..fc6e3753ec 100644 --- a/containers/ecr-viewer/src/app/tests/assets/BundlePatient.json +++ b/containers/ecr-viewer/src/app/tests/assets/BundlePatient.json @@ -177,6 +177,171 @@ "method": "PUT", "url": "Patient/6b6b3c4c-4884-4a96-b6ab-c46406839cea" } + }, + { + "fullUrl": "urn:uuid:b91cab75-e4a0-2227-79ec-44c9c8ea795a", + "resource": { + "resourceType": "Observation", + "id": "b91cab75-e4a0-2227-79ec-44c9c8ea795a", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observationresults" + ], + "source": "ecr" + }, + "identifier": [ + { + "system": "urn:oid:1.2.840.114350.1.13.66.2.7.7.698084.19222", + "value": "Z13909121" + } + ], + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "social-history" + } + ] + } + ], + "status": "final", + "code": { + "coding": [ + { + "code": "X-SDOH-19222", + "system": "urn:oid:1.2.840.114350.1.72.1.8", + "display": "Alcohol Comment" + } + ] + }, + "effectiveDateTime": "2022-11-04", + "valueString": "1-2 DRINKS 2 TO 4 TIMES A MONTH", + "subject": { + "reference": "Patient/5227f761-a6ea-461d-9523-8d9ee26eab84" + } + }, + "request": { + "method": "PUT", + "url": "Observation/b91cab75-e4a0-2227-79ec-44c9c8ea795a" + } + }, + { + "fullUrl": "urn:uuid:259df789-9da2-9b7c-bc69-a60c634860d8", + "resource": { + "resourceType": "Observation", + "id": "259df789-9da2-9b7c-bc69-a60c634860d8", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observationresults" + ], + "source": "ecr" + }, + "identifier": [ + { + "system": "urn:oid:1.2.840.114350.1.13.66.2.7.1.1040.13", + "value": "Z13909121^54660.98^897148007" + } + ], + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "social-history" + } + ] + } + ], + "status": "final", + "code": { + "coding": [ + { + "code": "897148007", + "system": "http://snomed.info/sct", + "display": "Alcoholic beverage intake" + }, + { + "code": "74013-4", + "system": "http://loinc.org", + "display": "Alcoholic drinks per day" + } + ] + }, + "effectiveDateTime": "2024-01-31", + "valueQuantity": { + "value": ".29", + "unit": "/d" + }, + "subject": { + "reference": "Patient/5227f761-a6ea-461d-9523-8d9ee26eab84" + } + }, + "request": { + "method": "PUT", + "url": "Observation/259df789-9da2-9b7c-bc69-a60c634860d8" + } + }, + { + "fullUrl": "urn:uuid:b467f663-f4f5-78af-a677-05b5545d8716", + "resource": { + "resourceType": "Observation", + "id": "b467f663-f4f5-78af-a677-05b5545d8716", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observationresults" + ], + "source": "ecr" + }, + "identifier": [ + { + "system": "urn:oid:1.2.840.114350.1.13.66.2.7.1.1040.12", + "value": "Z13909121^54660.98^897148007" + } + ], + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "social-history" + } + ] + } + ], + "status": "final", + "code": { + "coding": [ + { + "code": "897148007", + "system": "http://snomed.info/sct", + "display": "Alcoholic beverage intake" + }, + { + "code": "11331-6", + "system": "http://loinc.org", + "display": "History of Alcohol Use" + } + ] + }, + "effectiveDateTime": "2024-01-31", + "valueCodeableConcept": { + "coding": [ + { + "code": "219006", + "system": "http://snomed.info/sct", + "display": "Current drinker of alcohol (finding)" + } + ] + }, + "subject": { + "reference": "Patient/5227f761-a6ea-461d-9523-8d9ee26eab84" + } + }, + "request": { + "method": "PUT", + "url": "Observation/b467f663-f4f5-78af-a677-05b5545d8716" + } } ] } diff --git a/containers/ecr-viewer/src/app/tests/components/Demographics.test.tsx b/containers/ecr-viewer/src/app/tests/components/Demographics.test.tsx index 020f6465cd..d7a39cb759 100644 --- a/containers/ecr-viewer/src/app/tests/components/Demographics.test.tsx +++ b/containers/ecr-viewer/src/app/tests/components/Demographics.test.tsx @@ -35,6 +35,10 @@ describe("Demographics", () => { title: "County", value: "test", }, + { + title: "Country", + value: "USA", + }, { title: "Contact", value: "test contact" }, { title: "Emergency Contact", diff --git a/containers/ecr-viewer/src/app/tests/components/__snapshots__/AccordionContent.test.tsx.snap b/containers/ecr-viewer/src/app/tests/components/__snapshots__/AccordionContent.test.tsx.snap index 2a54fe1720..20fddbc244 100644 --- a/containers/ecr-viewer/src/app/tests/components/__snapshots__/AccordionContent.test.tsx.snap +++ b/containers/ecr-viewer/src/app/tests/components/__snapshots__/AccordionContent.test.tsx.snap @@ -439,6 +439,25 @@ exports[`Snapshot test for Accordion Content Given no data, info message for emp class="section__line_gray" /> +