Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
123 changes: 123 additions & 0 deletions components/measure-upload/EvaluationServiceVerifier.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Alert, Button, Group, Space, TextInput } from '@mantine/core';
import React, { useEffect, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { evaluationState } from '../../state/atoms/evaluation';
import { measureBundleState } from '../../state/atoms/measureBundle';
import { showNotification } from '@mantine/notifications';
import { IconAlertCircle, IconCircleCheck } from '@tabler/icons';

export default function EvaluationServiceVerifier() {
const [isLoadingId, setIsLoadingId] = useState(false);
const { content } = useRecoilValue(measureBundleState);
const [{ evaluationServiceUrl, evaluationMeasureId }, setEvaluation] = useRecoilState(evaluationState);

useEffect(() => {
// Called when the measure content value changes
setEvaluation(e => ({
...e,
evaluationMeasureId: ''
}));
}, [content, setEvaluation]);

/**
* Handles updating of the Evaluation Service url field and associated state variables
*/
function handleUrlChange(event: React.ChangeEvent<HTMLInputElement>) {
setEvaluation({
evaluationServiceUrl: event.currentTarget.value,
evaluationMeasureId: ''
});
}

/**
* Sends a `GET` request to the `/Measure` endpoint of the specified evaluation service URL.
* Parses and stores the url and the if of the matching (same url/version) Measure if successful
Comment thread
lmd59 marked this conversation as resolved.
Outdated
*/
async function verifyMeasure() {
setIsLoadingId(true);
let parsedEvaluationUrl = evaluationServiceUrl;
if (evaluationServiceUrl.slice(-1) === '/') {
parsedEvaluationUrl = evaluationServiceUrl.slice(0, -1);
}
try {
const response = await fetch(`${parsedEvaluationUrl}/Measure`);
const responseBody = await response.json();
if (response.status === 200) {
if (responseBody.entry.length === 0) {
showNotification({
icon: <IconAlertCircle />,
title: 'No measures found',
message: `Evalution service hosted at ${parsedEvaluationUrl} returned an empty
FHIR Bundle when queried for Measures. Ensure Measures are present on server.`,
color: 'red'
});
setIsLoadingId(false);
return;
}
const selectedMeasure = content?.entry?.find(e => e.resource?.resourceType === 'Measure')
Comment thread
zacharyrobin marked this conversation as resolved.
?.resource as fhir4.Measure;
// Currently taking the safe path and not assuming support for search -> finding the correct measure from a pull of all measures
const currentEvalMeasure = responseBody.entry.find(
(e: { resource: fhir4.Measure }) =>
e.resource.url === selectedMeasure.url && e.resource.version === selectedMeasure.version
)?.resource;
if (!currentEvalMeasure) {
showNotification({
icon: <IconAlertCircle />,
title: 'Failed to locate measure',
message: `Could not find measure with url ${selectedMeasure.url} and version ${selectedMeasure.version} on the selected evaluation service.`,
color: 'red'
});
setIsLoadingId(false);
return;
}

setEvaluation({ evaluationServiceUrl: parsedEvaluationUrl, evaluationMeasureId: currentEvalMeasure.id });
} else {
showNotification({
icon: <IconAlertCircle />,
title: 'Failed reaching out to server',
message: `Server responded with code: ${response.status}:${response.statusText}`,
color: 'red'
});
}
} catch (e) {
Comment thread
zacharyrobin marked this conversation as resolved.
showNotification({
icon: <IconAlertCircle />,
title: 'Failed reaching out to server',
message: `Could not connect to server with url: ${parsedEvaluationUrl}`,
color: 'red'
});
}
setIsLoadingId(false);
}

return (
<div>
<Group position="apart" align="flex-end">
<TextInput
label="Evaluation Service URL"
placeholder="http://example.com/fhir"
value={evaluationServiceUrl}
onChange={handleUrlChange}
style={{ flexGrow: 1 }}
/>
<Button
onClick={verifyMeasure}
loading={isLoadingId}
disabled={evaluationServiceUrl === '' || content === null}
>
Verify
</Button>
</Group>
<Space h="md" />
{evaluationMeasureId !== '' && (
<Group position="center" align="flex-end">
<Alert variant="light" color="green" icon={<IconCircleCheck />}>
Located Measure ID: {evaluationMeasureId}
</Alert>
</Group>
)}
</div>
);
}
10 changes: 9 additions & 1 deletion pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import MeasureFileUpload from '../components/measure-upload/MeasureFileUpload';
import DateSelectors from '../components/measure-upload/DateSelectors';
import { measureBundleState } from '../state/atoms/measureBundle';
import MeasureFileUploadHeader from '../components/utils/MeasureFileUploadHeader';
import MeasureRepositoryUploadHeader from '../components/utils/MeasureRespositoryUploadHeader';
import MeasureRepositoryUploadHeader from '../components/utils/MeasureRepositoryUploadHeader';
Comment thread
lmd59 marked this conversation as resolved.
import UploadErrorLog from '../components/measure-upload/UploadErrorLog';
import MeasureRepositoryUpload from '../components/measure-upload/MeasureRepositoryUpload';
import { MeasureUploadError } from '../util/measureUploadUtils';
import { trustMetaProfileState } from '../state/atoms/trustMetaProfile';
import { InfoCircle } from 'tabler-icons-react';
import EvaluationServiceVerifier from '../components/measure-upload/EvaluationServiceVerifier';

const useStyles = createStyles(theme => ({
headerContainer: {
Expand Down Expand Up @@ -132,6 +133,13 @@ const Home: NextPage = () => {
</Popover>
</Group>
</Grid.Col>
<Grid.Col sm={3} md={1}>
<Space h="md" />
<Text weight="lighter">(Optional) Select measure evaluation service</Text>
</Grid.Col>
<Grid.Col sm={3} md={2}>
<EvaluationServiceVerifier />
</Grid.Col>
</Grid>
<Divider className={classes.divider} />
<Group position="right">
Expand Down
17 changes: 17 additions & 0 deletions state/atoms/evaluation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { atom } from 'recoil';

interface EvaluationStateType {
evaluationServiceUrl: string;
evaluationMeasureId: string;
}

/**
* Atom tracking and controlling the value of uploaded measure bundle

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it was maybe copied from measureBundle.ts, so I would update it so it includes info about the evaluation service url!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! Thanks for catching!

Comment thread
lmd59 marked this conversation as resolved.
Outdated
*/
export const evaluationState = atom<EvaluationStateType>({
key: 'evaluationState',
default: {
evaluationServiceUrl: '',
evaluationMeasureId: ''
}
});