Skip to content

Commit

Permalink
refactor code, update README
Browse files Browse the repository at this point in the history
  • Loading branch information
Amy Chen authored and Amy Chen committed Jun 5, 2024
1 parent be4f52c commit d2a8bfd
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 267 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Parameters for the app are stored in [environmental variables](http://man7.org/l
| `REACT_APP_DISABLE_HEADER` | Define whether the header element should appear, default is `false` |`true`, `false`
| `REACT_APP_DISABLE_NAV` | Define whether the navigation menu element should appear, default is `false` |`true`, `false`
| `REACT_APP_MATOMO_SITE_ID` | Define the site id used to for tracking via MATOMO | Example: `24`

| `REACT_APP_EPIC_QUERIES` | Define whether the site is quering data from EPIC | example: `true`, default is `false` | [`true`, `false`]
### Using with Public SMART Sandbox
A public [SMART<sup>&reg;</sup> App Launcher](https://launch.smarthealthit.org/index.html) is available for sandbox tesing of SMART on FHIR apps with synthetic data.

Expand Down
2 changes: 1 addition & 1 deletion src/components/sections/MedicalHistory.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function MedicalHistory(props) {
item.condition = o.displayText || "--";
item.onsetDateTime = o.onsetDateTimeDisplayText;
item.recordedDate = o.recordedDateTimeDisplayText;
item.status = o.status || "--";
item.status = o.status ? String(o.status).toLowerCase() : "--";
return item;
})
.sort((a, b) => {
Expand Down
2 changes: 1 addition & 1 deletion src/config/sections_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import SummarizeIcon from "@mui/icons-material/Summarize";
import Box from "@mui/material/Box";
import CircularProgress from "@mui/material/CircularProgress";
import Stack from "@mui/material/Stack";
import { getResourcesByResourceType } from "../util/util";
import { getResourcesByResourceType } from "../util/fhirUtil";

const renderLoader = () => (
<Stack
Expand Down
2 changes: 1 addition & 1 deletion src/context/QuestionnaireListProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function QuestionnaireListProvider({ children }) {
getEnvQuestionnaireList()
);
const [exactMatch, setExactMatch] = useState(
getEnv("REACT_APP_EPIC_QUERIES")
String(getEnv("REACT_APP_EPIC_QUERIES")) === "true"
);
const { client, patient } = useContext(FhirClientContext);

Expand Down
6 changes: 4 additions & 2 deletions src/hooks/useFetchResources.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import {
getChartConfig,
getElmDependencies,
getInterventionLogicLib,
isNumber,
} from "../util/util";
import {
getResourcesByResourceType,
getFhirResourcesFromQueryResult,
getFHIRResourcesToLoad,
getFHIRResourcePaths,
isNumber,
} from "../util/util";
} from "../util/fhirUtil";
import qConfig from "../config/questionnaire_config";

export default function useFetchResources() {
Expand Down
6 changes: 4 additions & 2 deletions src/models/Observation.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
getCorrectedISODate,
getFhirItemValue,
getFhirComponentDisplays,
} from "../util/util";
} from "../util/fhirUtil";
import {
getCorrectedISODate,
} from "../util/util"

class Observation {
constructor(dataObj) {
Expand Down
226 changes: 226 additions & 0 deletions src/util/fhirUtil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import {
getEnv,
getSectionsToShow,
hasValue,
} from "./util";
import { DEFAULT_OBSERVATION_CATEGORIES } from "../consts/consts";

export function getFHIRResourcesToLoad() {
const defaultList = [
"Condition",
"Observation",
"Questionnaire",
"QuestionnaireResponse",
];
const resourcesToLoad = getEnv("REACT_APP_FHIR_RESOURCES");
const sections = getSectionsToShow();
const envResourcesToLoad = resourcesToLoad ? resourcesToLoad.split(",") : [];
let resourcesForSection = [];
if (sections && sections.length) {
sections.forEach((section) => {
if (section.resources && section.resources.length) {
resourcesForSection = [...resourcesForSection, ...section.resources];
}
});
}
const combinedResources = [...envResourcesToLoad, ...resourcesForSection];
const allResources = [...new Set(combinedResources)];
const resources = allResources.length
? [...new Set([...allResources, ...defaultList])]
: defaultList;

//console.log("Resources to load : ", resources);
return resources;
}

export function getFHIRResourceQueryParams(resourceType, options) {
if (!resourceType) return null;
let paramsObj = {
_sort: "-_lastUpdated",
_count: 200,
};
const queryOptions = options ? options : {};
switch (String(resourceType).toLowerCase()) {
case "careplan":
const envCategory = getEnv("REACT_APP_FHIR_CAREPLAN_CATEGORY");
if (queryOptions.patientId) {
paramsObj["subject"] = `Patient/${queryOptions.patientId}`;
}
if (envCategory) {
paramsObj["category:text"] = envCategory;
}
break;
case "questionnaire":
if (
queryOptions.questionnaireList &&
Array.isArray(queryOptions.questionnaireList) &&
queryOptions.questionnaireList.length
) {
paramsObj[queryOptions.exactMatch ? "_id" : "name:contains"] =
queryOptions.questionnaireList.join(",");
}
break;
case "observation":
const envObCategories = getEnv("REACT_APP_FHIR_OBSERVATION_CATEGORIES");
const observationCategories = envObCategories
? envObCategories
: DEFAULT_OBSERVATION_CATEGORIES;
paramsObj["category"] = observationCategories;
if (queryOptions.patientId) {
paramsObj["patient"] = `Patient/${queryOptions.patientId}`;
}
break;
default:
if (queryOptions.patientId) {
paramsObj["patient"] = `Patient/${queryOptions.patientId}`;
}
}
return paramsObj;
}

export function getFHIRResourcePaths(patientId, resourcesToLoad, options) {
if (!patientId) return [];
const resources =
resourcesToLoad && Array.isArray(resourcesToLoad) && resourcesToLoad.length
? resourcesToLoad
: getFHIRResourcesToLoad();
return resources.map((resource) => {
let path = `/${resource}`;
const paramsObj = getFHIRResourceQueryParams(resource, {
...(options ? options : {}),
patientId: patientId,
});
if (paramsObj) {
const searchParams = new URLSearchParams(paramsObj);
path = path + "?" + searchParams.toString();
}
return {
resourceType: resource,
resourcePath: path,
};
});
}

export function getResourcesByResourceType(patientBundle, resourceType) {
if (!patientBundle || !Array.isArray(patientBundle) || !patientBundle.length)
return null;
return patientBundle
.filter((item) => {
return (
item.resource &&
String(item.resource.resourceType).toLowerCase() ===
String(resourceType).toLowerCase()
);
})
.map((item) => item.resource);
}

export function getQuestionnairesByCarePlan(arrCarePlans) {
if (!arrCarePlans) return [];
let activities = [];
arrCarePlans.forEach((item) => {
if (item.resource.activity) {
activities = [...activities, ...item.resource.activity];
}
});
let qList = [];
activities.forEach((a) => {
if (
a.detail &&
a.detail.instantiatesCanonical &&
a.detail.instantiatesCanonical.length
) {
const qId = a.detail.instantiatesCanonical[0].split("/")[1];
if (qId && qList.indexOf(qId) === -1) qList.push(qId);
}
});
return qList;
}

export function getFhirResourcesFromQueryResult(result) {
let bundle = [];
if (!result) return [];
if (result.resourceType === "Bundle" && result.entry) {
result.entry.forEach((o) => {
if (o && o.resource) bundle.push({ resource: o.resource });
});
} else if (Array.isArray(result)) {
result.forEach((o) => {
if (o.resourceType) bundle.push({ resource: o });
});
} else {
bundle.push({ resource: result });
}
return bundle;
}

export function getFhirItemValue(item) {
if (!item) return null;
if (hasValue(item.valueQuantity)) {
if (item.valueQuantity.unit) {
return [item.valueQuantity.value, item.valueQuantity.unit].join(" ");
}
return item.valueQuantity.value;
}
if (hasValue(item.valueString)) {
if (hasValue(item.valueString.value)) return String(item.valueString.value);
return item.valueString;
}
if (hasValue(item.valueBoolean)) {
if (hasValue(item.valueBoolean.value))
return String(item.valueBoolean.value);
return String(item.valueBoolean);
}
if (hasValue(item.valueInteger)) {
if (hasValue(item.valueInteger.value)) return item.valueInteger.value;
return item.valueInteger;
}
if (hasValue(item.valueDecimal)) {
if (hasValue(item.valueDecimal.value)) return item.valueDecimal.value;
return item.valueDecimal;
}
if (item.valueDate) {
if (hasValue(item.valueDate.value)) return item.valueDate.value;
return item.valueDate;
}
if (hasValue(item.valueDateTime)) {
if (item.valueDateTime.value) return item.valueDateTime.value;
return item.valueDateTime;
}
if (item.valueCodeableConcept) {
if (item.valueCodeableConcept.text) {
return item.valueCodeableConcept.text;
} else if (
item.valueCodeableConcept.coding &&
Array.isArray(item.valueCodeableConcept.coding) &&
item.valueCodeableConcept.coding.length
) {
return item.valueCodeableConcept.coding[0].display;
}
return null;
}
// need to handle date/time value

return null;
}
export function getFhirComponentDisplays(item) {
let displayText = getFhirItemValue(item);
if (!item || !item.component || !item.component.length) return displayText;
const componentDisplay = item.component
.map((o) => {
const textDisplay = o.code && o.code.text ? o.code.text : null;
const valueDisplay = getFhirItemValue(o);
if (hasValue(valueDisplay))
return textDisplay
? [textDisplay, valueDisplay].join(": ")
: valueDisplay;
return "";
})
.join(", ");
if (displayText && componentDisplay) {
return [displayText, componentDisplay].join(", ");
}
if (componentDisplay) return componentDisplay;
if (displayText) return displayText;
return null;
}
Loading

0 comments on commit d2a8bfd

Please sign in to comment.