Skip to content

Commit

Permalink
Merge pull request #879 from aehrc/feature/extract
Browse files Browse the repository at this point in the history
Feature/extract
  • Loading branch information
fongsean authored Jun 21, 2024
2 parents 40952bd + ad43e61 commit 663e00f
Show file tree
Hide file tree
Showing 53 changed files with 2,137 additions and 71 deletions.
10 changes: 8 additions & 2 deletions apps/smart-forms-app/e2e/_pre-run.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
*/

import { expect, test } from '@playwright/test';
import { PLAYWRIGHT_APP_URL, PLAYWRIGHT_EHR_URL, PLAYWRIGHT_FORMS_SERVER_URL } from './globals';
import {
LAUNCH_PARAM_WITHOUT_Q,
PLAYWRIGHT_APP_URL,
PLAYWRIGHT_EHR_URL,
PLAYWRIGHT_FORMS_SERVER_URL
} from './globals';

test('launch without questionnaire context, select a questionnaire and create a new response', async ({
page
Expand All @@ -25,7 +30,8 @@ test('launch without questionnaire context, select a questionnaire and create a
const fetchQPromise = page.waitForResponse(
`${PLAYWRIGHT_FORMS_SERVER_URL}/Questionnaire?_count=100&_sort=-date&`
);
const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIiIsZmFsc2Vd`;

const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=${LAUNCH_PARAM_WITHOUT_Q}`;
await page.goto(launchUrl);
expect((await fetchQPromise).status()).toBe(200);

Expand Down
4 changes: 2 additions & 2 deletions apps/smart-forms-app/e2e/dashboard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import { expect, test } from '@playwright/test';
import { PLAYWRIGHT_APP_URL, PLAYWRIGHT_FORMS_SERVER_URL } from './globals';
import { LAUNCH_PARAM_WITHOUT_Q, PLAYWRIGHT_APP_URL, PLAYWRIGHT_FORMS_SERVER_URL } from './globals';

const questionnaireTitle = 'Aboriginal and Torres Strait Islander Health Check';

Expand All @@ -25,7 +25,7 @@ test.beforeEach(async ({ page }) => {
const fetchQPromise = page.waitForResponse(
`${PLAYWRIGHT_FORMS_SERVER_URL}/Questionnaire?_count=100&_sort=-date&`
);
const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIiIsZmFsc2Vd`;
const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=${LAUNCH_PARAM_WITHOUT_Q}`;
await page.goto(launchUrl);
expect((await fetchQPromise).status()).toBe(200);

Expand Down
8 changes: 8 additions & 0 deletions apps/smart-forms-app/e2e/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@ export const PLAYWRIGHT_FORMS_SERVER_URL = 'https://smartforms.csiro.au/api/fhir
export const PLAYWRIGHT_APP_URL = process.env.CI
? 'http://localhost:4173'
: 'http://localhost:5173';

export const LAUNCH_PARAM_WITHOUT_Q = process.env.CI
? 'WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIiIsZmFsc2Vd'
: 'WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjUxNzMvIiwiMWZmN2JkYzItMzZiMi00MzAzLThjMDUtYzU3MzQyYzViMDQzIiwiIiwiIiwiIiwiIiwwLDEsIiIsZmFsc2Vd';

export const LAUNCH_PARAM_WITH_Q = process.env.CI
? 'WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIntcInJvbGVcIjpcInF1ZXN0aW9ubmFpcmUtcmVuZGVyLW9uLWxhdW5jaFwiLFwiY2Fub25pY2FsXCI6XCJodHRwOi8vd3d3LmhlYWx0aC5nb3YuYXUvYXNzZXNzbWVudHMvbWJzLzcxNXwwLjEuMC1hc3NlbWJsZWRcIixcInR5cGVcIjpcIlF1ZXN0aW9ubmFpcmVcIn0iLGZhbHNlXQ'
: 'WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjUxNzMvIiwiMWZmN2JkYzItMzZiMi00MzAzLThjMDUtYzU3MzQyYzViMDQzIiwiIiwiIiwiIiwiIiwwLDEsIntcInJvbGVcIjpcInF1ZXN0aW9ubmFpcmUtcmVuZGVyLW9uLWxhdW5jaFwiLFwiY2Fub25pY2FsXCI6XCJodHRwOi8vd3d3LmhlYWx0aC5nb3YuYXUvYXNzZXNzbWVudHMvbWJzLzcxNXwwLjEuMC1hc3NlbWJsZWRcIixcInR5cGVcIjpcIlF1ZXN0aW9ubmFpcmVcIn0iLGZhbHNlXQ';
4 changes: 2 additions & 2 deletions apps/smart-forms-app/e2e/saving.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import { expect, test } from '@playwright/test';
import { PLAYWRIGHT_APP_URL, PLAYWRIGHT_EHR_URL } from './globals';
import { LAUNCH_PARAM_WITH_Q, PLAYWRIGHT_APP_URL, PLAYWRIGHT_EHR_URL } from './globals';

const questionnaireTitle = 'Aboriginal and Torres Strait Islander Health Check';

Expand All @@ -25,7 +25,7 @@ test.beforeEach(async ({ page }) => {
const populatePromise = page.waitForResponse(
new RegExp(/^https:\/\/proxy\.smartforms\.io\/v\/r4\/fhir\/(Observation|Condition)\?.+$/)
);
const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIntcInJvbGVcIjpcInF1ZXN0aW9ubmFpcmUtcmVuZGVyLW9uLWxhdW5jaFwiLFwiY2Fub25pY2FsXCI6XCJodHRwOi8vd3d3LmhlYWx0aC5nb3YuYXUvYXNzZXNzbWVudHMvbWJzLzcxNXwwLjEuMC1hc3NlbWJsZWRcIixcInR5cGVcIjpcIlF1ZXN0aW9ubmFpcmVcIn0iLGZhbHNlXQ`;
const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=${LAUNCH_PARAM_WITH_Q}`;
await page.goto(launchUrl);
const populateResponse = await populatePromise;
expect(populateResponse.status()).toBe(200);
Expand Down
2 changes: 1 addition & 1 deletion apps/smart-forms-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@aehrc/sdc-assemble": "^1.2.0",
"@aehrc/sdc-populate": "^2.2.3",
"@aehrc/smart-forms-renderer": "^0.35.7",
"@aehrc/smart-forms-renderer": "^0.35.8",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@fontsource/material-icons": "^5.0.18",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import CreateNewResponseButton from '../Buttons/CreateNewResponseButton.tsx';
import ViewExistingResponsesButton from '../Buttons/ViewExistingResponsesButton.tsx';
import ClearIcon from '@mui/icons-material/Clear';
import useSmartClient from '../../../../../../hooks/useSmartClient.ts';
import useDebugMode from '../../../../../../hooks/useDebugMode.ts';
import GoToSdcIdeButton from '../Buttons/GoToSdcIdeButton.tsx';

interface QuestionnaireListToolbarButtonsProps {
onClearSelection: () => void;
Expand All @@ -31,13 +29,9 @@ function QuestionnaireListToolbarButtons(props: QuestionnaireListToolbarButtonsP
const { onClearSelection } = props;

const { smartClient } = useSmartClient();
const { debugModeEnabled } = useDebugMode();

const isNotLaunched = !smartClient;

return (
<Box display="flex" alignItems="center" columnGap={2}>
{isNotLaunched && debugModeEnabled ? <GoToSdcIdeButton /> : null}
<CreateNewResponseButton />

{smartClient ? <ViewExistingResponsesButton /> : null}
Expand Down
69 changes: 69 additions & 0 deletions apps/smart-forms-app/src/features/playground/api/extract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2024 Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { HEADERS } from '../../../api/headers.ts';
import type { Bundle, Questionnaire, StructureMap } from 'fhir/r4';
import * as FHIR from 'fhirclient';
import { FORMS_SERVER_URL } from '../../../globals.ts';

export async function fetchTargetStructureMap(
questionnaire: Questionnaire
): Promise<StructureMap | null> {
let targetStructureMapCanonical = questionnaire.extension?.find(
(extension) =>
extension.url ===
'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-targetStructureMap'
)?.valueCanonical;

if (!targetStructureMapCanonical) {
return null;
}

targetStructureMapCanonical = targetStructureMapCanonical.replace('|', '&version=');
const requestUrl = `/StructureMap?url=${targetStructureMapCanonical}&_sort=_lastUpdated`;
const resource = await FHIR.client(FORMS_SERVER_URL).request({
url: requestUrl,
headers: HEADERS
});

// Response isn't a resource, exit early
if (!resource.resourceType) {
return null;
}

if (resource.resourceType === 'Bundle') {
return resource.entry?.find((entry: any) => entry.resource?.resourceType === 'StructureMap')

Check warning on line 49 in apps/smart-forms-app/src/features/playground/api/extract.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
?.resource as StructureMap;
}

if (resource.resourceType === 'StructureMap') {
return resource as StructureMap;
}

return null;
}

export function extractedResourceIsBatchBundle(
extractedResource: any

Check warning on line 61 in apps/smart-forms-app/src/features/playground/api/extract.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
): extractedResource is Bundle {
return (
!!extractedResource &&
!!extractedResource.resourceType &&
extractedResource.resourceType === 'Bundle' &&
(extractedResource.type === 'transaction' || extractedResource.type === 'batch')
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2024 Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// @ts-ignore
import React from 'react';
import { CircularProgress, Fade, IconButton, Tooltip } from '@mui/material';
import Typography from '@mui/material/Typography';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import { FORMS_SERVER_URL } from '../../../globals.ts';

interface ExtractForPlaygroundProps {
extractEnabled: boolean;
isExtracting: boolean;
onExtract: () => void;
}

function ExtractButtonForPlayground(props: ExtractForPlaygroundProps) {
const { extractEnabled, isExtracting, onExtract } = props;

const toolTipText = extractEnabled
? 'Perform $extract'
: `The current questionnaire does not have a target StructureMap for $extract, or the target StructureMap cannot be found on ${FORMS_SERVER_URL}`;

return (
<>
<Tooltip title={toolTipText} placement="bottom-end">
<span>
<IconButton
disabled={isExtracting || !extractEnabled}
onClick={onExtract}
size="small"
color="primary"
data-test="extract-button-playground">
{isExtracting ? (
<CircularProgress size={20} color="inherit" sx={{ mb: 0.5 }} />
) : (
<CloudUploadIcon />
)}
</IconButton>
</span>
</Tooltip>
{isExtracting ? (
<Fade in={true} timeout={100}>
<Typography variant="body2" color="text.secondary">
Performing extraction...
</Typography>
</Fade>
) : null}
</>
);
}

export default ExtractButtonForPlayground;
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,20 @@ interface Props {
jsonString: string;
onJsonStringChange: (jsonString: string) => void;
buildingState: 'idle' | 'building' | 'built';
fhirServerUrl: string;
onBuildForm: (jsonString: string) => unknown;
onDestroyForm: () => unknown;
}

function JsonEditor(props: Props) {
const { jsonString, onJsonStringChange, buildingState, onBuildForm, onDestroyForm } = props;
const {
jsonString,
onJsonStringChange,
buildingState,
fhirServerUrl,
onBuildForm,
onDestroyForm
} = props;

const [view, setView] = useState<'editor' | 'storeState'>('editor');
const [selectedStore, setSelectedStore] = useState<StateStore>('questionnaireResponseStore');
Expand Down Expand Up @@ -78,7 +86,7 @@ function JsonEditor(props: Props) {
onClick={() => {
setView('storeState');
}}>
See store state
See advanced properties
</Button>
) : (
<Stack direction="row" alignItems="center" gap={0.55}>
Expand All @@ -99,6 +107,7 @@ function JsonEditor(props: Props) {
<ToggleButton value="questionnaireStore">Q</ToggleButton>
<ToggleButton value="questionnaireResponseStore">QR</ToggleButton>
<ToggleButton value="terminologyServerStore">Terminology</ToggleButton>
<ToggleButton value="extractedResource">Extracted</ToggleButton>
</ToggleButtonGroup>
</Stack>
)}
Expand Down Expand Up @@ -131,7 +140,7 @@ function JsonEditor(props: Props) {
/>
) : (
<Box sx={{ height: '100%', overflow: 'auto' }}>
<StoreStateViewer selectedStore={selectedStore} />
<StoreStateViewer selectedStore={selectedStore} fhirServerUrl={fhirServerUrl} />
</Box>
)}
</Box>
Expand Down
Loading

0 comments on commit 663e00f

Please sign in to comment.