Skip to content

Commit

Permalink
Merge pull request #1860 from UCSF-IGHS/fix-hiv-ART-columns
Browse files Browse the repository at this point in the history
(fix) Transfer special column logic to the commons lib (ART columns, Status Tag columns, Secondary concept columns)
  • Loading branch information
hadijahkyampeire authored Jun 10, 2024
2 parents a3ca4d0 + 16a2e0a commit 92ffac5
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { Tag } from '@carbon/react';
import { getObsFromEncounter, findObs } from './encounter-list-utils';

export const renderTag = (encounter, concept, statusColorMappings) => {
const columnStatus = getObsFromEncounter(encounter, concept);
const columnStatusObs = findObs(encounter, concept);
if (columnStatus == '--') {
return '--';
} else {
return (
<Tag type={statusColorMappings[columnStatusObs?.value?.uuid]} title={columnStatus} style={{ minWidth: '80px' }}>
{columnStatus}
</Tag>
);
}
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { getObsFromEncounter, getMultipleObsFromEncounter } from './encounter-list-utils';
import {
getObsFromEncounter,
getMultipleObsFromEncounter,
resolveValueUsingMappings,
getConceptFromMappings,
} from './encounter-list-utils';
import { renderTag } from './encounter-list-component-util';

interface MenuProps {
menuId: string;
Expand All @@ -25,6 +31,7 @@ interface ColumnDefinition {
title: string;
isComplex?: boolean;
concept?: string;
secondaryConcept?: string;
multipleConcepts?: Array<string>;
fallbackConcepts?: Array<string>;
actionOptions?: Array<ActionProps>;
Expand All @@ -34,6 +41,9 @@ interface ColumnDefinition {
type?: string;
isLink?: boolean;
useMultipleObs?: boolean;
valueMappings?: Record<string, string>;
conceptMappings?: Array<string>;
statusColorMappings?: Record<string, string>;
}

interface LaunchOptions {
Expand Down Expand Up @@ -92,8 +102,22 @@ export const getTabColumns = (columnsDefinition: Array<ColumnDefinition>) => {
});
}
return [...baseActions, ...conditionalActions];
} else if (column.statusColorMappings) {
return renderTag(encounter, column.concept, column.statusColorMappings);
} else if (column.useMultipleObs === true) {
return getMultipleObsFromEncounter(encounter, column.multipleConcepts);
} else if (column.valueMappings) {
return resolveValueUsingMappings(encounter, column.concept, column.valueMappings);
} else if (column.conceptMappings) {
const concept = getConceptFromMappings(encounter, column.conceptMappings);
return getObsFromEncounter(
encounter,
concept,
column.isDate,
column.isTrueFalseConcept,
column.type,
column.fallbackConcepts,
);
} else {
return getObsFromEncounter(
encounter,
Expand Down
25 changes: 25 additions & 0 deletions packages/esm-commons-lib/src/utils/encounter-list-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ export function getObsFromEncounters(encounters, obsConcept) {
return getObsFromEncounter(filteredEnc, obsConcept);
}

export function resolveValueUsingMappings(encounter, concept, mappings) {
const obs = findObs(encounter, concept);
return obs ? mappings[obs.value.uuid] || obs.value : '--';
}

export function getConceptFromMappings(encounter, concepts) {
for (const concept of concepts) {
const obs = findObs(encounter, concept);
if (obs && obs.value) {
return concept;
}
}
return null;
}

export function getMultipleObsFromEncounter(encounter, obsConcepts: Array<string>) {
let observations = [];
obsConcepts.map((concept) => {
Expand All @@ -47,6 +62,7 @@ export function getObsFromEncounter(
isTrueFalseConcept?: Boolean,
type?: string,
fallbackConcepts?: Array<string>,
secondaryConcept?: string,
) {
let obs = findObs(encounter, obsConcept);

Expand All @@ -66,6 +82,15 @@ export function getObsFromEncounter(
return encounter.encounterProviders.map((p) => p.provider.name).join(' | ');
}

if (secondaryConcept && typeof obs.value === 'object' && obs.value.names) {
const primaryValue =
obs.value.names.find((conceptName) => conceptName.conceptNameType === 'SHORT')?.name || obs.value.name.name;
if (primaryValue === 'Other non-coded') {
const secondaryObs = findObs(encounter, secondaryConcept);
return secondaryObs ? secondaryObs.value : '--';
}
}

if (!obs && fallbackConcepts?.length >= 1) {
const concept = fallbackConcepts.find((c) => findObs(encounter, c) != null);
obs = findObs(encounter, concept);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
{
"id": "covidVaccineType",
"title": "Vaccine Administered",
"concept": "1410AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"concept": "e41fbe17-4aee-4a44-950b-6676d6e0ede2",
"secondaryConcept": "0cc868bd-e9dd-4b59-b278-f923afe22d82"
},
{
"id": "actions",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import React from 'react';
import {
EncounterList,
findObs,
getObsFromEncounter,
getMenuItemTabConfiguration,
} from '@ohri/openmrs-esm-ohri-commons-lib';
import { useConfig } from '@openmrs/esm-framework';
import { EncounterList, getMenuItemTabConfiguration } from '@ohri/openmrs-esm-ohri-commons-lib';

import covidVaccinationsSchemaConfig from './covid-vaccinations-schema.json';

interface CovidVaccinationsWidgetProps {
Expand All @@ -15,38 +10,8 @@ interface CovidVaccinationsWidgetProps {
export const covidFormSlot = 'hts-encounter-form-slot';

const CovidVaccinations: React.FC<CovidVaccinationsWidgetProps> = ({ patientUuid }) => {
const config = useConfig();
const tabs = getMenuItemTabConfiguration(covidVaccinationsSchemaConfig);

const getVaccineAdministeredValue = (encounter) => {
const obs = findObs(encounter, config.obsConcepts.covidVaccineAdministeredConcept_UUID);
if (typeof obs !== undefined && obs) {
if (typeof obs.value === 'object') {
if (obs !== undefined) {
const vaccineNAME =
obs.value.names?.find((conceptName) => conceptName.conceptNameType === 'SHORT')?.name ||
obs.value.name.name;
if (vaccineNAME === 'Other non-coded') {
return getObsFromEncounter(encounter, config.obsConcepts.covidVaccineConcept_UUID);
}
}
}
}
return getObsFromEncounter(encounter, config.obsConcepts.covidVaccineAdministeredConcept_UUID);
};

function updateAdministeredVaccineColumn(tabs) {
for (let tab of tabs) {
for (let column of tab.columns) {
if (column.key === 'covidVaccineType') {
column.getValue = (encounter) => getVaccineAdministeredValue(encounter);
}
}
}
}

updateAdministeredVaccineColumn(tabs);

return (
<>
{tabs.map((tab) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@
"id": "labStatus",
"title": "Status",
"concept": "6681366c-2174-489a-b951-13a1404935bf",
"type": "tag"
"type": "tag",
"statusColorMappings": {
"1118AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "green",
"1267AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "green",
"165170AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "purple",
"162866AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "blue"
}
},
{
"id": "labTestResult",
Expand Down Expand Up @@ -137,7 +143,13 @@
"id": "labStatus",
"title": "Status",
"concept": "6681366c-2174-489a-b951-13a1404935bf",
"type": "tag"
"type": "tag",
"statusColorMappings": {
"1118AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "green",
"1267AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "green",
"165170AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "purple",
"162866AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "blue"
}
},
{
"id": "actions",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import React from 'react';
import styles from './covid.scss';
import { Tabs, Tab, Tag, TabList, TabPanels, TabPanel } from '@carbon/react';
import {
EncounterList,
findObs,
getObsFromEncounter,
getMenuItemTabConfiguration,
} from '@ohri/openmrs-esm-ohri-commons-lib';
import { Tabs, Tab, TabList, TabPanels, TabPanel } from '@carbon/react';
import { EncounterList, getObsFromEncounter, getMenuItemTabConfiguration } from '@ohri/openmrs-esm-ohri-commons-lib';
import { useConfig } from '@openmrs/esm-framework';
import covidLabTestSchemaConfig from './lab-results-schema-config.json';

Expand All @@ -16,47 +11,15 @@ export const covidEncounterRepresentation =
'encounterProviders:(uuid,provider:(uuid,name)),' +
'obs:(uuid,obsDatetime,concept:(uuid,name:(uuid,name)),value:(uuid,name:(uuid,name))))';

const statusColorMap = {
'1118AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'green', // not done
'1267AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'green', // completed
'165170AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'purple', // cancelled
'162866AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'blue', // pending
};
interface CovidLabWidgetProps {
patientUuid: string;
}

const renderTag = (encounter, concept) => {
const status = getObsFromEncounter(encounter, concept);
const statusObs = findObs(encounter, concept);
if (status == '--') {
return '--';
} else {
return (
<Tag type={statusColorMap[statusObs?.value?.uuid]} title={status} className={styles.statusTag}>
{status}
</Tag>
);
}
};

const CovidLabResults: React.FC<CovidLabWidgetProps> = ({ patientUuid }) => {
const config = useConfig();

const tabs = getMenuItemTabConfiguration(covidLabTestSchemaConfig);

function updateStatusTagColumn(tabs, renderTag) {
for (let tab of tabs) {
for (let column of tab.columns) {
if (column.key === 'labStatus') {
column.getValue = (encounter) => renderTag(encounter, column.concept);
}
}
}
}

updateStatusTagColumn(tabs, renderTag);

let pendingLabOrdersFilter = (encounter) => {
return getObsFromEncounter(encounter, config.obsConcepts.covidTestStatusConcept_UUID) === 'Pending';
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import React from 'react';
import { Tabs, Tab, TabList, TabPanels, TabPanel } from '@carbon/react';
import { EncounterList, getMenuItemTabConfiguration } from '@ohri/openmrs-esm-ohri-commons-lib';
import { Tabs, Tab, TabList, TabPanels, TabPanel, Tag } from '@carbon/react';
import {
EncounterList,
getMenuItemTabConfiguration,
getObsFromEncounter,
findObs,
} from '@ohri/openmrs-esm-ohri-commons-lib';
import partnerNotificationsConfigSchema from './patner-notification-config.json';

import styles from '../common.scss';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
"id": "hivStatus",
"title": "Status",
"concept": "1436AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"type": "hivStatus"
"type": "hivStatus",
"statusColorMappings": {
"703AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "red",
"664AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "gray",
"1067AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "purple"
}
},
{
"id": "actions",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,27 @@
"id": "initiationDate",
"isDate": true,
"title": "Date",
"concept": "159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"type": "artDate",
"fallbackConcepts": [
"conceptMappings": [
"159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"162572AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"164516AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"164431AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"162572AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"160738AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
]
},
{
"id": "therapyPlan",
"title": "Therapy Plan",
"type": "artTherapy",
"concept": "7557d77c-172b-4673-9335-67a38657dd01"
"concept": "7557d77c-172b-4673-9335-67a38657dd01",
"valueMappings": {
"1256AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "Start ART",
"1258AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "Substitute ART Regimen",
"1259AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "Switch ART Regimen Line",
"1260AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": "Stop ART",
"3e69cb60-2943-410f-83d4-b359ae83fefd": "Restart ART therapy"
}
},
{
"id": "regimen",
Expand All @@ -105,7 +112,12 @@
{
"id": "reason",
"title": "Reason",
"concept": "159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"conceptMappings": [
"160568AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"160562AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"163513AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"161011AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
],
"type": "artReason"
},
{
Expand Down Expand Up @@ -216,7 +228,6 @@
{
"id": "verified",
"title": "Verified",
"type": "transferVerified",
"concept": "797e0073-1f3f-46b1-8b1a-8cdad134d2b3"
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface OverviewListProps {

const ProgramManagementSummary: React.FC<OverviewListProps> = ({ patientUuid }) => {
const tabs = getMenuItemTabConfiguration(programManagementTabConfigSchema);

return (
<div className={styles.tabContainer}>
<Tabs>
Expand Down

0 comments on commit 92ffac5

Please sign in to comment.