@aehrc/smart-forms-renderer 0.35.8 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/utils/calculatedExpression.js +15 -43
- package/lib/utils/calculatedExpression.js.map +1 -1
- package/lib/utils/manageForm.d.ts +7 -1
- package/lib/utils/manageForm.js +9 -0
- package/lib/utils/manageForm.js.map +1 -1
- package/lib/utils/questionnaireStoreUtils/resolveValueSets.js +8 -3
- package/lib/utils/questionnaireStoreUtils/resolveValueSets.js.map +1 -1
- package/lib/utils/removeEmptyAnswers.js +19 -6
- package/lib/utils/removeEmptyAnswers.js.map +1 -1
- package/lib/utils/repopulateIntoResponse.d.ts +1 -3
- package/lib/utils/repopulateIntoResponse.js +2 -31
- package/lib/utils/repopulateIntoResponse.js.map +1 -1
- package/lib/utils/tabs.js +6 -4
- package/lib/utils/tabs.js.map +1 -1
- package/lib/utils/updateQr.d.ts +9 -0
- package/lib/utils/updateQr.js +55 -0
- package/lib/utils/updateQr.js.map +1 -0
- package/lib/utils/valueSet.js +4 -2
- package/lib/utils/valueSet.js.map +1 -1
- package/package.json +5 -4
- package/src/utils/calculatedExpression.ts +26 -60
- package/src/utils/manageForm.ts +11 -1
- package/src/utils/questionnaireStoreUtils/resolveValueSets.ts +10 -6
- package/src/utils/removeEmptyAnswers.ts +22 -6
- package/src/utils/repopulateIntoResponse.ts +5 -58
- package/src/utils/tabs.ts +6 -4
- package/src/utils/updateQr.ts +88 -0
- package/src/utils/valueSet.ts +4 -2
- package/stats.html +4842 -0
- package/stats1.html +4842 -0
- package/stats3.html +4842 -0
- package/lib/utils/answerExpression.d.ts +0 -5
- package/lib/utils/answerExpression.js +0 -52
- package/lib/utils/answerExpression.js.map +0 -1
- package/lib/utils/dynamicValueSet.d.ts +0 -5
- package/lib/utils/dynamicValueSet.js +0 -96
- package/lib/utils/dynamicValueSet.js.map +0 -1
- package/lib/utils/fhirpathAsyncUtils/fhirpath-async.d.ts +0 -14
- package/lib/utils/fhirpathAsyncUtils/fhirpath-async.js +0 -431
- package/lib/utils/fhirpathAsyncUtils/fhirpath-async.js.map +0 -1
- package/lib/utils/fhirpathAsyncUtils/outcome-utils.d.ts +0 -3
- package/lib/utils/fhirpathAsyncUtils/outcome-utils.js +0 -41
- package/lib/utils/fhirpathAsyncUtils/outcome-utils.js.map +0 -1
- package/lib/utils/fhirpathAsyncUtils/test-questionnaire.d.ts +0 -1
- package/lib/utils/fhirpathAsyncUtils/test-questionnaire.js +0 -379
- package/lib/utils/fhirpathAsyncUtils/test-questionnaire.js.map +0 -1
|
@@ -34,6 +34,7 @@ import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
|
|
|
34
34
|
import { updateQrItemsInGroup } from './qrItem';
|
|
35
35
|
import cloneDeep from 'lodash.clonedeep';
|
|
36
36
|
import dayjs from 'dayjs';
|
|
37
|
+
import { updateQuestionnaireResponse } from './updateQr';
|
|
37
38
|
|
|
38
39
|
interface EvaluateInitialCalculatedExpressionsParams {
|
|
39
40
|
initialResponse: QuestionnaireResponse;
|
|
@@ -164,15 +165,6 @@ export function initialiseCalculatedExpressionValues(
|
|
|
164
165
|
populatedResponse: QuestionnaireResponse,
|
|
165
166
|
calculatedExpressions: Record<string, CalculatedExpression[]>
|
|
166
167
|
): QuestionnaireResponse {
|
|
167
|
-
if (
|
|
168
|
-
!questionnaire.item ||
|
|
169
|
-
questionnaire.item.length === 0 ||
|
|
170
|
-
!populatedResponse.item ||
|
|
171
|
-
populatedResponse.item.length === 0
|
|
172
|
-
) {
|
|
173
|
-
return populatedResponse;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
168
|
// Filter calculated expressions, only preserve key-value pairs with values
|
|
177
169
|
const calculatedExpressionsWithValues: Record<string, CalculatedExpression[]> = {};
|
|
178
170
|
for (const linkId in calculatedExpressions) {
|
|
@@ -185,80 +177,54 @@ export function initialiseCalculatedExpressionValues(
|
|
|
185
177
|
}
|
|
186
178
|
}
|
|
187
179
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
item: []
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const updatedTopLevelQRItem = initialiseItemCalculatedExpressionValueRecursive(
|
|
198
|
-
topLevelQItem,
|
|
199
|
-
populatedTopLevelQrItem,
|
|
200
|
-
calculatedExpressionsWithValues
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
if (Array.isArray(updatedTopLevelQRItem)) {
|
|
204
|
-
if (updatedTopLevelQRItem.length > 0) {
|
|
205
|
-
topLevelQrItems.push(...updatedTopLevelQRItem);
|
|
206
|
-
}
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (updatedTopLevelQRItem) {
|
|
211
|
-
topLevelQrItems.push(updatedTopLevelQRItem);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return { ...populatedResponse, item: topLevelQrItems };
|
|
180
|
+
return updateQuestionnaireResponse(
|
|
181
|
+
questionnaire,
|
|
182
|
+
populatedResponse,
|
|
183
|
+
initialiseItemCalculatedExpressionValueRecursive,
|
|
184
|
+
calculatedExpressionsWithValues
|
|
185
|
+
);
|
|
216
186
|
}
|
|
217
187
|
|
|
218
188
|
function initialiseItemCalculatedExpressionValueRecursive(
|
|
219
189
|
qItem: QuestionnaireItem,
|
|
220
|
-
|
|
190
|
+
qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null,
|
|
221
191
|
calculatedExpressions: Record<string, CalculatedExpression[]>
|
|
222
|
-
): QuestionnaireResponseItem
|
|
192
|
+
): QuestionnaireResponseItem | QuestionnaireResponseItem[] | null {
|
|
193
|
+
// For repeat groups
|
|
194
|
+
const hasMultipleAnswers = Array.isArray(qrItemOrItems);
|
|
195
|
+
if (hasMultipleAnswers) {
|
|
196
|
+
return qrItemOrItems;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const qrItem = qrItemOrItems;
|
|
223
200
|
const childQItems = qItem.item;
|
|
224
201
|
if (childQItems && childQItems.length > 0) {
|
|
225
|
-
// iterate through items of item recursively
|
|
226
202
|
const childQrItems = qrItem?.item ?? [];
|
|
227
|
-
// const updatedChildQrItems: QuestionnaireResponseItem[] = [];
|
|
228
|
-
|
|
229
|
-
// FIXME Not implemented for repeat groups
|
|
230
|
-
if (qItem.type === 'group' && qItem.repeats) {
|
|
231
|
-
return qrItem ?? null;
|
|
232
|
-
}
|
|
233
203
|
|
|
234
204
|
const indexMap = mapQItemsIndex(qItem);
|
|
235
205
|
const qrItemsByIndex = getQrItemsIndex(childQItems, childQrItems, indexMap);
|
|
236
206
|
|
|
237
207
|
// Otherwise loop through qItem as usual
|
|
238
208
|
for (const [index, childQItem] of childQItems.entries()) {
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
// FIXME Not implemented for repeat groups
|
|
242
|
-
if (Array.isArray(childQrItem)) {
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
209
|
+
const childQRItemOrItems = qrItemsByIndex[index];
|
|
245
210
|
|
|
246
|
-
const
|
|
211
|
+
const updatedChildQRItemOrItems = initialiseItemCalculatedExpressionValueRecursive(
|
|
247
212
|
childQItem,
|
|
248
|
-
|
|
213
|
+
childQRItemOrItems ?? null,
|
|
249
214
|
calculatedExpressions
|
|
250
215
|
);
|
|
251
216
|
|
|
252
217
|
// FIXME Not implemented for repeat groups
|
|
253
|
-
if (Array.isArray(
|
|
218
|
+
if (Array.isArray(updatedChildQRItemOrItems)) {
|
|
254
219
|
continue;
|
|
255
220
|
}
|
|
256
221
|
|
|
257
|
-
|
|
222
|
+
const updatedChildQRItem = updatedChildQRItemOrItems;
|
|
223
|
+
if (updatedChildQRItem) {
|
|
258
224
|
updateQrItemsInGroup(
|
|
259
|
-
|
|
225
|
+
updatedChildQRItem,
|
|
260
226
|
null,
|
|
261
|
-
|
|
227
|
+
updatedChildQRItem ?? { linkId: qItem.linkId, text: qItem.text, item: [] },
|
|
262
228
|
indexMap
|
|
263
229
|
);
|
|
264
230
|
}
|
|
@@ -287,7 +253,7 @@ function getCalculatedExpressionAnswer(
|
|
|
287
253
|
|
|
288
254
|
function constructGroupItem(
|
|
289
255
|
qItem: QuestionnaireItem,
|
|
290
|
-
qrItem: QuestionnaireResponseItem |
|
|
256
|
+
qrItem: QuestionnaireResponseItem | null,
|
|
291
257
|
calculatedExpressions: Record<string, CalculatedExpression[]>
|
|
292
258
|
): QuestionnaireResponseItem | null {
|
|
293
259
|
const calculatedExpressionAnswer = getCalculatedExpressionAnswer(qItem, calculatedExpressions);
|
|
@@ -317,7 +283,7 @@ function constructGroupItem(
|
|
|
317
283
|
|
|
318
284
|
function constructSingleItem(
|
|
319
285
|
qItem: QuestionnaireItem,
|
|
320
|
-
qrItem: QuestionnaireResponseItem |
|
|
286
|
+
qrItem: QuestionnaireResponseItem | null,
|
|
321
287
|
calculatedExpressions: Record<string, CalculatedExpression[]>
|
|
322
288
|
): QuestionnaireResponseItem | null {
|
|
323
289
|
const calculatedExpressionAnswer = getCalculatedExpressionAnswer(qItem, calculatedExpressions);
|
package/src/utils/manageForm.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
|
|
1
|
+
import type { Questionnaire, QuestionnaireResponse, QuestionnaireResponseItem } from 'fhir/r4';
|
|
2
2
|
import {
|
|
3
3
|
questionnaireResponseStore,
|
|
4
4
|
questionnaireStore,
|
|
@@ -30,6 +30,7 @@ export async function buildForm(
|
|
|
30
30
|
terminologyServerUrl?: string,
|
|
31
31
|
additionalVariables?: Record<string, object>
|
|
32
32
|
): Promise<void> {
|
|
33
|
+
// Reset terminology server
|
|
33
34
|
if (terminologyServerUrl) {
|
|
34
35
|
terminologyServerStore.getState().setUrl(terminologyServerUrl);
|
|
35
36
|
} else {
|
|
@@ -120,3 +121,12 @@ export function removeEmptyAnswersFromResponse(
|
|
|
120
121
|
enableWhenExpressions
|
|
121
122
|
});
|
|
122
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Check if a QuestionnaireResponseItem has either an item or an answer property.
|
|
127
|
+
*
|
|
128
|
+
* @author Sean Fong
|
|
129
|
+
*/
|
|
130
|
+
export function qrItemHasItemsOrAnswer(qrItem: QuestionnaireResponseItem): boolean {
|
|
131
|
+
return (!!qrItem.item && qrItem.item.length > 0) || (!!qrItem.answer && qrItem.answer.length > 0);
|
|
132
|
+
}
|
|
@@ -47,17 +47,21 @@ export async function resolveValueSets(
|
|
|
47
47
|
const resolvedPromises = await resolveValueSetPromises(valueSetPromises);
|
|
48
48
|
|
|
49
49
|
for (const valueSetUrl in resolvedPromises) {
|
|
50
|
-
const valueSet = resolvedPromises[valueSetUrl]
|
|
50
|
+
const valueSet = resolvedPromises[valueSetUrl]?.valueSet;
|
|
51
51
|
|
|
52
52
|
if (valueSet) {
|
|
53
53
|
if (valueSetToXFhirQueryVariableNameMap[valueSetUrl]) {
|
|
54
54
|
// valueSetUrl is in x-fhir-query variables, save to variable
|
|
55
55
|
const variableName = valueSetToXFhirQueryVariableNameMap[valueSetUrl];
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
if (variableName) {
|
|
57
|
+
const variable = variables.xFhirQueryVariables[variableName];
|
|
58
|
+
if (variable) {
|
|
59
|
+
variables.xFhirQueryVariables[variableName] = {
|
|
60
|
+
...variable,
|
|
61
|
+
result: resolvedPromises[valueSetUrl]?.valueSet
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
61
65
|
} else {
|
|
62
66
|
// valueSetUrl is in x-fhir-query variables, save to preprocessedValueSetCodings
|
|
63
67
|
processedValueSetCodings[valueSetUrl] = getValueSetCodings(valueSet);
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
import type { EnableWhenExpressions, EnableWhenItems } from '../interfaces/enableWhen.interface';
|
|
25
25
|
import { isHiddenByEnableWhen } from './qItem';
|
|
26
26
|
import cloneDeep from 'lodash.clonedeep';
|
|
27
|
+
import { qrItemHasItemsOrAnswer } from './manageForm';
|
|
27
28
|
|
|
28
29
|
interface removeEmptyAnswersParams {
|
|
29
30
|
questionnaire: Questionnaire;
|
|
@@ -60,21 +61,31 @@ export function removeEmptyAnswers(params: removeEmptyAnswersParams): Questionna
|
|
|
60
61
|
return updatedQuestionnaireResponse;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
const newQuestionnaireResponse: QuestionnaireResponse = { ...questionnaireResponse, item: [] };
|
|
65
|
+
for (const [i, topLevelQRItem] of topLevelQRItems.entries()) {
|
|
64
66
|
const qItem = topLevelQItems[i];
|
|
67
|
+
if (!qItem) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// If QR item don't have either item.item and item.answer, continue
|
|
72
|
+
if (!qrItemHasItemsOrAnswer(topLevelQRItem)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
65
76
|
const newTopLevelQRItem = removeEmptyAnswersFromItemRecursive({
|
|
66
77
|
qItem,
|
|
67
|
-
qrItem,
|
|
78
|
+
qrItem: topLevelQRItem,
|
|
68
79
|
enableWhenIsActivated,
|
|
69
80
|
enableWhenItems,
|
|
70
81
|
enableWhenExpressions
|
|
71
82
|
});
|
|
72
|
-
if (newTopLevelQRItem &&
|
|
73
|
-
|
|
83
|
+
if (newTopLevelQRItem && newQuestionnaireResponse.item) {
|
|
84
|
+
newQuestionnaireResponse.item.push(newTopLevelQRItem);
|
|
74
85
|
}
|
|
75
|
-
}
|
|
86
|
+
}
|
|
76
87
|
|
|
77
|
-
return
|
|
88
|
+
return newQuestionnaireResponse;
|
|
78
89
|
}
|
|
79
90
|
|
|
80
91
|
interface removeEmptyAnswersFromItemRecursiveParams {
|
|
@@ -90,6 +101,11 @@ function removeEmptyAnswersFromItemRecursive(
|
|
|
90
101
|
): QuestionnaireResponseItem | null {
|
|
91
102
|
const { qItem, qrItem, enableWhenIsActivated, enableWhenItems, enableWhenExpressions } = params;
|
|
92
103
|
|
|
104
|
+
// If QR item don't have either item.item and item.answer, return null
|
|
105
|
+
if (!qrItemHasItemsOrAnswer(qrItem)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
93
109
|
const qItems = qItem.item;
|
|
94
110
|
const qrItems = qrItem.item;
|
|
95
111
|
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Questionnaire,
|
|
3
|
-
QuestionnaireItem,
|
|
4
|
-
QuestionnaireResponse,
|
|
5
|
-
QuestionnaireResponseItem
|
|
6
|
-
} from 'fhir/r4';
|
|
1
|
+
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
7
2
|
import type { ItemToRepopulate } from './repopulateItems';
|
|
8
3
|
import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
|
|
9
4
|
import { isSpecificItemControl } from './itemControl';
|
|
10
5
|
import { questionnaireResponseStore, questionnaireStore } from '../stores';
|
|
6
|
+
import { updateQuestionnaireResponse } from './updateQr';
|
|
11
7
|
|
|
12
8
|
/**
|
|
13
9
|
* Re-populate checked items in the re-population dialog into the current QuestionnaireResponse
|
|
@@ -18,68 +14,19 @@ export function repopulateResponse(checkedItemsToRepopulate: Record<string, Item
|
|
|
18
14
|
const sourceQuestionnaire = questionnaireStore.getState().sourceQuestionnaire;
|
|
19
15
|
const updatableResponse = questionnaireResponseStore.getState().updatableResponse;
|
|
20
16
|
|
|
21
|
-
return
|
|
17
|
+
return updateQuestionnaireResponse(
|
|
22
18
|
sourceQuestionnaire,
|
|
23
19
|
updatableResponse,
|
|
20
|
+
repopulateItemRecursive,
|
|
24
21
|
checkedItemsToRepopulate
|
|
25
22
|
);
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
export function repopulateItemsIntoResponse(
|
|
29
|
-
questionnaire: Questionnaire,
|
|
30
|
-
updatableResponse: QuestionnaireResponse,
|
|
31
|
-
checkedItemsToRepopulate: Record<string, ItemToRepopulate>
|
|
32
|
-
): QuestionnaireResponse {
|
|
33
|
-
if (
|
|
34
|
-
!questionnaire.item ||
|
|
35
|
-
questionnaire.item.length === 0 ||
|
|
36
|
-
!updatableResponse.item ||
|
|
37
|
-
updatableResponse.item.length === 0
|
|
38
|
-
) {
|
|
39
|
-
return updatableResponse;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const qItemsIndexMap = mapQItemsIndex(questionnaire);
|
|
43
|
-
const topLevelQRItemsByIndex = getQrItemsIndex(
|
|
44
|
-
questionnaire.item,
|
|
45
|
-
updatableResponse.item,
|
|
46
|
-
qItemsIndexMap
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const topLevelQrItems: QuestionnaireResponseItem[] = [];
|
|
50
|
-
for (const [index, topLevelQItem] of questionnaire.item.entries()) {
|
|
51
|
-
const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? {
|
|
52
|
-
linkId: topLevelQItem.linkId,
|
|
53
|
-
text: topLevelQItem.text,
|
|
54
|
-
item: []
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const updatedTopLevelQRItem = repopulateItemRecursive(
|
|
58
|
-
topLevelQItem,
|
|
59
|
-
topLevelQRItemOrItems,
|
|
60
|
-
checkedItemsToRepopulate
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
if (Array.isArray(updatedTopLevelQRItem)) {
|
|
64
|
-
if (updatedTopLevelQRItem.length > 0) {
|
|
65
|
-
topLevelQrItems.push(...updatedTopLevelQRItem);
|
|
66
|
-
}
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (updatedTopLevelQRItem) {
|
|
71
|
-
topLevelQrItems.push(updatedTopLevelQRItem);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return { ...updatableResponse, item: topLevelQrItems };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
25
|
function repopulateItemRecursive(
|
|
79
26
|
qItem: QuestionnaireItem,
|
|
80
27
|
qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null,
|
|
81
28
|
checkedItemsToRepopulate: Record<string, ItemToRepopulate>
|
|
82
|
-
): QuestionnaireResponseItem
|
|
29
|
+
): QuestionnaireResponseItem | QuestionnaireResponseItem[] | null {
|
|
83
30
|
// For repeat groups
|
|
84
31
|
const hasMultipleAnswers = Array.isArray(qrItemOrItems);
|
|
85
32
|
if (hasMultipleAnswers) {
|
package/src/utils/tabs.ts
CHANGED
|
@@ -38,12 +38,14 @@ export function getFirstVisibleTab(
|
|
|
38
38
|
return false;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const singleItem = singleItems[tabLinkId];
|
|
42
|
+
if (singleItem) {
|
|
43
|
+
return singleItem.isEnabled;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
const singleExpression = singleExpressions[tabLinkId];
|
|
47
|
+
if (singleExpression) {
|
|
48
|
+
return singleExpression.isEnabled;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
return true;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Commonwealth Scientific and Industrial Research
|
|
3
|
+
* Organisation (CSIRO) ABN 41 687 119 230.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
Questionnaire,
|
|
20
|
+
QuestionnaireItem,
|
|
21
|
+
QuestionnaireResponse,
|
|
22
|
+
QuestionnaireResponseItem
|
|
23
|
+
} from 'fhir/r4';
|
|
24
|
+
import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
|
|
25
|
+
import { qrItemHasItemsOrAnswer } from './manageForm';
|
|
26
|
+
|
|
27
|
+
export type RepopulateFunction<T> = (
|
|
28
|
+
qItem: QuestionnaireItem,
|
|
29
|
+
qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null,
|
|
30
|
+
extraData: T
|
|
31
|
+
) => QuestionnaireResponseItem | QuestionnaireResponseItem[] | null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A generic (and safe) way to update a QuestionnaireResponse given a recursive function and a set of data i.e. Record<linkId, calculated expression values>, Record<linkId, re-populated values>
|
|
35
|
+
* This function relies heavily on mapQItemsIndex() and getQrItemsIndex() to accurately pinpoint the locations of QR items based on their positions in the Q, taking into account repeating group answers, non-filled questions, etc
|
|
36
|
+
*
|
|
37
|
+
* @author Sean Fong
|
|
38
|
+
*/
|
|
39
|
+
export function updateQuestionnaireResponse<T>(
|
|
40
|
+
questionnaire: Questionnaire,
|
|
41
|
+
questionnaireResponse: QuestionnaireResponse,
|
|
42
|
+
recursiveUpdateFunction: RepopulateFunction<T>,
|
|
43
|
+
extraData: T
|
|
44
|
+
) {
|
|
45
|
+
if (
|
|
46
|
+
!questionnaire.item ||
|
|
47
|
+
questionnaire.item.length === 0 ||
|
|
48
|
+
!questionnaireResponse.item ||
|
|
49
|
+
questionnaireResponse.item.length === 0
|
|
50
|
+
) {
|
|
51
|
+
return questionnaireResponse;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const qItemsIndexMap = mapQItemsIndex(questionnaire);
|
|
55
|
+
const topLevelQRItemsByIndex = getQrItemsIndex(
|
|
56
|
+
questionnaire.item,
|
|
57
|
+
questionnaireResponse.item,
|
|
58
|
+
qItemsIndexMap
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const topLevelQrItems = [];
|
|
62
|
+
for (const [index, topLevelQItem] of questionnaire.item.entries()) {
|
|
63
|
+
const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? {
|
|
64
|
+
linkId: topLevelQItem.linkId,
|
|
65
|
+
text: topLevelQItem.text,
|
|
66
|
+
item: []
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const updatedTopLevelQRItem = recursiveUpdateFunction(
|
|
70
|
+
topLevelQItem,
|
|
71
|
+
topLevelQRItemOrItems,
|
|
72
|
+
extraData
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (Array.isArray(updatedTopLevelQRItem)) {
|
|
76
|
+
if (updatedTopLevelQRItem.length > 0) {
|
|
77
|
+
topLevelQrItems.push(...updatedTopLevelQRItem);
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (updatedTopLevelQRItem && qrItemHasItemsOrAnswer(updatedTopLevelQRItem)) {
|
|
83
|
+
topLevelQrItems.push(updatedTopLevelQRItem);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { ...questionnaireResponse, item: topLevelQrItems };
|
|
88
|
+
}
|
package/src/utils/valueSet.ts
CHANGED
|
@@ -52,8 +52,10 @@ export function getValueSetPromise(url: string, terminologyServerUrl: string): P
|
|
|
52
52
|
|
|
53
53
|
if (url.includes('ValueSet/$expand?url=')) {
|
|
54
54
|
const splitUrl = url.split('ValueSet/$expand?url=');
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
if (splitUrl[0] && splitUrl[1]) {
|
|
56
|
+
terminologyServerUrl = splitUrl[0];
|
|
57
|
+
valueSetUrl = splitUrl[1];
|
|
58
|
+
}
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
valueSetUrl = valueSetUrl.replace('|', '&version=');
|