fhirsmith 0.9.5 → 0.9.7
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/CHANGELOG.md +38 -0
- package/config-template.json +2 -1
- package/library/folder-content-loader.js +91 -0
- package/library/regex-utilities.js +49 -12
- package/npmprojector/npmprojector.js +2 -6
- package/package.json +2 -2
- package/publisher/publisher.js +105 -12
- package/registry/registry.js +6 -6
- package/server.js +6 -2
- package/test-scripts/repro-re2-wasm-leak.js +8 -7
- package/translations/Messages.properties +1 -1
- package/tx/cs/cs-cs.js +8 -0
- package/tx/cs/cs-loinc.js +13 -12
- package/tx/cs/cs-omop.js +24 -23
- package/tx/cs/cs-provider-list.js +2 -1
- package/tx/cs/cs-snomed.js +142 -59
- package/tx/cs/cs-unii.js +11 -11
- package/tx/data/snomed-testing.cache +0 -0
- package/tx/library/canonical-resource.js +4 -2
- package/tx/library/designations.js +27 -20
- package/tx/library/renderer.js +303 -22
- package/tx/library/ucum-types.js +4 -1
- package/tx/library.js +65 -21
- package/tx/operation-context.js +52 -23
- package/tx/params.js +36 -8
- package/tx/problems.js +0 -4
- package/tx/provider.js +7 -3
- package/tx/tx-html.js +7 -0
- package/tx/tx.js +24 -13
- package/tx/vs/vs-vsac.js +157 -9
- package/tx/workers/expand.js +100 -96
- package/tx/workers/lookup.js +6 -0
- package/tx/workers/read.js +1 -1
- package/tx/workers/translate.js +21 -29
- package/tx/workers/validate.js +18 -10
- package/tx/workers/worker.js +5 -1
- package/tx/xversion/xv-bundle.js +1 -2
- package/tx/xversion/xv-codesystem.js +5 -2
- package/tx/xversion/xv-parameters.js +4 -4
- package/tx/xversion/xv-resource.js +2 -2
- package/tx/xversion/xv-terminologyCapabilities.js +11 -6
- package/tx/xversion/xv-valueset.js +7 -7
- package/publisher/task-draft.js +0 -458
package/tx/workers/expand.js
CHANGED
|
@@ -647,11 +647,11 @@ class ValueSetExpander {
|
|
|
647
647
|
} else if (cs.contentMode() === 'supplement') {
|
|
648
648
|
throw new Issue('error', 'business-rule', null, null, 'The code system definition for ' + cset.system + ' defines a supplement, so this expansion cannot be performed', 'invalid');
|
|
649
649
|
} else if (cs.contentMode() === 'fragment') {
|
|
650
|
-
this.addParamUri(exp, 'used-fragment', cs.system()
|
|
650
|
+
this.addParamUri(exp, 'used-fragment', cs.system()+ (cs.version() ? '|' + cs.version(): ""));
|
|
651
651
|
Extensions.addBoolean(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
|
|
652
652
|
Extensions.addString(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason","This extension is based on a fragment of the code system " + cset.system);
|
|
653
653
|
} else {
|
|
654
|
-
this.addParamUri(exp, cs.contentMode(), cs.system() + '|' + cs.version());
|
|
654
|
+
this.addParamUri(exp, cs.contentMode(), cs.system() + cs.system()+ (cs.version() ? '|' + cs.version(): ""));
|
|
655
655
|
Extensions.addBoolean(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
|
|
656
656
|
Extensions.addString(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason","This extension is based on a fragment of the code system " + cset.system);
|
|
657
657
|
}
|
|
@@ -948,7 +948,7 @@ class ValueSetExpander {
|
|
|
948
948
|
let vs = await this.worker.findValueSet(s, '', vsSrc);
|
|
949
949
|
const ivs = new ImportedValueSet(await this.expandValueSet(s, '', vs, filter, notClosed));
|
|
950
950
|
this.checkResourceCanonicalStatus(expansion, ivs.valueSet, this.valueSet);
|
|
951
|
-
if (!vs.isContained) {
|
|
951
|
+
if (!vs.isContained && ivs.valueSet.vurl) {
|
|
952
952
|
this.addParamUri(expansion, 'used-valueset', ivs.valueSet.vurl);
|
|
953
953
|
}
|
|
954
954
|
valueSets.push(ivs);
|
|
@@ -961,124 +961,128 @@ class ValueSetExpander {
|
|
|
961
961
|
const cs = await this.worker.findCodeSystem(cset.system, cset.version, this.params, ['complete', 'fragment'], false,
|
|
962
962
|
true, true, null, this.requiredSupplements);
|
|
963
963
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
964
|
+
if (cs == null) {
|
|
965
|
+
// nothing?
|
|
966
|
+
} else {
|
|
967
|
+
this.worker.checkSupplements(cs, cset, this.requiredSupplements, this.usedSupplements);
|
|
968
|
+
this.checkResourceCanonicalStatus(expansion, cs, this.valueSet);
|
|
969
|
+
const sv = this.canonical(await cs.system(), await cs.version());
|
|
970
|
+
this.addParamUri(expansion, 'used-codesystem', sv);
|
|
968
971
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
972
|
+
for (const u of cset.valueSet || []) {
|
|
973
|
+
this.worker.deadCheck('processCodes#3');
|
|
974
|
+
const s = this.worker.pinValueSet(u);
|
|
975
|
+
this.worker.opContext.log('import value set ' + s);
|
|
976
|
+
let vs = await this.worker.findValueSet(s, '', vsSrc);
|
|
977
|
+
const ivs = new ImportedValueSet(await this.expandValueSet(s, '', vs, filter, notClosed));
|
|
978
|
+
this.checkResourceCanonicalStatus(expansion, ivs.valueSet, this.valueSet);
|
|
979
|
+
if (!vs.isContained) {
|
|
980
|
+
this.addParamUri(expansion, 'used-valueset', this.worker.makeVurl(ivs.valueSet));
|
|
981
|
+
}
|
|
982
|
+
valueSets.push(ivs);
|
|
978
983
|
}
|
|
979
|
-
valueSets.push(ivs);
|
|
980
|
-
}
|
|
981
984
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
985
|
+
if (!cset.concept && !cset.filter) {
|
|
986
|
+
this.worker.opContext.log('handle system');
|
|
987
|
+
if (!cset.valueSet) {
|
|
988
|
+
if (!this.excludeSpecialCase) {
|
|
989
|
+
// excluding a whole system - we don't list the codes in this case
|
|
990
|
+
this.excludedSystems.add(cset.system + (this.doingVersion && cset.version ? '|' + cset.version : ''));
|
|
991
|
+
} else {
|
|
992
|
+
const iter = await cs.iteratorAll();
|
|
993
|
+
if (iter) {
|
|
994
|
+
let c = await cs.nextContext(iter);
|
|
995
|
+
while (c) {
|
|
996
|
+
this.worker.deadCheck('processCodes#3aa');
|
|
997
|
+
this.excludeCode(cs, cs.system(), cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
|
|
998
|
+
c = await cs.nextContext(iter);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
988
1002
|
} else {
|
|
1003
|
+
if (cs.isNotClosed(filter)) {
|
|
1004
|
+
if (cs.specialEnumeration()) {
|
|
1005
|
+
Extensions.addBoolean(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
|
|
1006
|
+
Extensions.addString(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason", 'The code System "' + cs.system() + " has a grammar and so has infinite members. This extension is based on " + cs.specialEnumeration());
|
|
1007
|
+
} else {
|
|
1008
|
+
throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly', null, 422).withDiagnostics(this.worker.opContext.diagnostics());
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
989
1012
|
const iter = await cs.iteratorAll();
|
|
990
1013
|
if (iter) {
|
|
991
1014
|
let c = await cs.nextContext(iter);
|
|
992
1015
|
while (c) {
|
|
993
|
-
this.worker.deadCheck('processCodes#
|
|
994
|
-
|
|
1016
|
+
this.worker.deadCheck('processCodes#3a');
|
|
1017
|
+
if (await this.passesFilters(cs, c, prep, filters, 0)) {
|
|
1018
|
+
this.excludeCode(cs, cs.system(), cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
|
|
1019
|
+
}
|
|
995
1020
|
c = await cs.nextContext(iter);
|
|
996
1021
|
}
|
|
997
1022
|
}
|
|
998
1023
|
}
|
|
999
|
-
} else {
|
|
1000
|
-
if (cs.isNotClosed(filter)) {
|
|
1001
|
-
if (cs.specialEnumeration()) {
|
|
1002
|
-
Extensions.addBoolean(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
|
|
1003
|
-
Extensions.addString(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason", 'The code System "' + cs.system() + " has a grammar and so has infinite members. This extension is based on " + cs.specialEnumeration());
|
|
1004
|
-
} else {
|
|
1005
|
-
throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly', null, 422).withDiagnostics(this.worker.opContext.diagnostics());
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
const iter = await cs.iteratorAll();
|
|
1010
|
-
if (iter) {
|
|
1011
|
-
let c = await cs.nextContext(iter);
|
|
1012
|
-
while (c) {
|
|
1013
|
-
this.worker.deadCheck('processCodes#3a');
|
|
1014
|
-
if (await this.passesFilters(cs, c, prep, filters, 0)) {
|
|
1015
|
-
this.excludeCode(cs, cs.system(), cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
|
|
1016
|
-
}
|
|
1017
|
-
c = await cs.nextContext(iter);
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
1024
|
}
|
|
1021
|
-
}
|
|
1022
1025
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1026
|
+
if (cset.concept) {
|
|
1027
|
+
this.worker.opContext.log('iterate concepts');
|
|
1028
|
+
const cds = new Designations(this.worker.i18n.languageDefinitions);
|
|
1029
|
+
for (const cc of cset.concept) {
|
|
1030
|
+
this.worker.deadCheck('processCodes#3');
|
|
1031
|
+
cds.clear();
|
|
1032
|
+
Extensions.checkNoModifiers(cc, 'ValueSetExpander.processCodes', 'set concept reference', vsSrc.vurl);
|
|
1033
|
+
const cctxt = await cs.locate(cc.code, this.allAltCodes);
|
|
1034
|
+
if (cctxt && cctxt.context && (!this.params.activeOnly || !await cs.isInactive(cctxt.context)) && await this.passesFilters(cs, cctxt.context, prep, filters, 0)) {
|
|
1035
|
+
if (filter.passesDesignations(cds) || filter.passes(cc.code)) {
|
|
1036
|
+
let ov = Extensions.readString(cc, 'http://hl7.org/fhir/StructureDefinition/itemWeight');
|
|
1037
|
+
if (!ov) {
|
|
1038
|
+
ov = await cs.itemWeight(cctxt.context);
|
|
1039
|
+
}
|
|
1040
|
+
this.excludeCode(cs, await cs.system(), await cs.version(), cc.code, expansion, valueSets, vsSrc.url);
|
|
1036
1041
|
}
|
|
1037
|
-
this.excludeCode(cs, await cs.system(), await cs.version(), cc.code, expansion, valueSets, vsSrc.url);
|
|
1038
1042
|
}
|
|
1039
1043
|
}
|
|
1040
1044
|
}
|
|
1041
|
-
}
|
|
1042
1045
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1046
|
+
if (cset.filter) {
|
|
1047
|
+
this.worker.opContext.log('prep filters');
|
|
1048
|
+
const prep = await cs.getPrepContext(true);
|
|
1049
|
+
if (!filter.isNull) {
|
|
1050
|
+
await cs.searchFilter(filter, prep, true);
|
|
1051
|
+
}
|
|
1049
1052
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1053
|
+
if (cs.specialEnumeration()) {
|
|
1054
|
+
await cs.specialFilter(prep, true);
|
|
1055
|
+
Extensions.addBoolean(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
|
|
1056
|
+
Extensions.addString(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason", 'The code System "' + cs.system() + " has a grammar and so has infinite members. This extension is based on " + cs.specialEnumeration());
|
|
1057
|
+
}
|
|
1055
1058
|
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1059
|
+
let first = true;
|
|
1060
|
+
for (let fc of cset.filter) {
|
|
1061
|
+
this.worker.deadCheck('processCodes#4a');
|
|
1062
|
+
Extensions.checkNoModifiers(fc, 'ValueSetExpander.processCodes', 'filter', vsSrc.vurl);
|
|
1063
|
+
await cs.filter(prep, first, fc.property, fc.op, fc.value);
|
|
1064
|
+
first = false;
|
|
1065
|
+
}
|
|
1063
1066
|
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1067
|
+
this.worker.opContext.log('iterate filters');
|
|
1068
|
+
const fset = await cs.executeFilters(prep);
|
|
1069
|
+
if (await cs.filtersNotClosed(prep)) {
|
|
1070
|
+
notClosed.value = true;
|
|
1071
|
+
}
|
|
1072
|
+
//let count = 0;
|
|
1073
|
+
while (await cs.filterMore(prep, fset[0])) {
|
|
1074
|
+
this.worker.deadCheck('processCodes#5');
|
|
1075
|
+
const c = await cs.filterConcept(prep, fset[0]);
|
|
1076
|
+
const ok = (!this.params.activeOnly || !await cs.isInactive(c)) && (await this.passesFilters(cs, c, prep, fset, 1));
|
|
1077
|
+
if (ok) {
|
|
1078
|
+
//count++;
|
|
1079
|
+
if (this.passesImports(valueSets, await cs.system(), await cs.code(c), 0)) {
|
|
1080
|
+
this.excludeCode(cs, await cs.system(), await cs.version(), await cs.code(c), expansion, null, vsSrc.url);
|
|
1081
|
+
}
|
|
1078
1082
|
}
|
|
1079
1083
|
}
|
|
1084
|
+
this.worker.opContext.log('iterate filters finished');
|
|
1080
1085
|
}
|
|
1081
|
-
this.worker.opContext.log('iterate filters finished');
|
|
1082
1086
|
}
|
|
1083
1087
|
}
|
|
1084
1088
|
}
|
package/tx/workers/lookup.js
CHANGED
|
@@ -374,6 +374,12 @@ class LookupWorker extends TerminologyWorker {
|
|
|
374
374
|
});
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
if (designation.status && designation.status !== 'active') {
|
|
378
|
+
designationParts.push({
|
|
379
|
+
name: 'status',
|
|
380
|
+
valueCode: designation.status
|
|
381
|
+
});
|
|
382
|
+
}
|
|
377
383
|
designationParts.push({
|
|
378
384
|
name: 'value',
|
|
379
385
|
valueString: designation.value
|
package/tx/workers/read.js
CHANGED
package/tx/workers/translate.js
CHANGED
|
@@ -205,10 +205,8 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
205
205
|
targetSystem = params.get('targetSystem');
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
-
let explicit = true;
|
|
209
208
|
// If no explicit concept map, we need to find one based on source/target
|
|
210
209
|
if (conceptMaps.length == 0) {
|
|
211
|
-
explicit = false;
|
|
212
210
|
if (reverse) {
|
|
213
211
|
await this.findConceptMapsInAdditionalResources(conceptMaps,targetSystem, targetScope, sourceScope, coding.system);
|
|
214
212
|
await this.provider.findConceptMapForTranslation(this.opContext, conceptMaps, targetSystem, targetScope, sourceScope, coding.system, coding.code);
|
|
@@ -222,7 +220,7 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
222
220
|
}
|
|
223
221
|
|
|
224
222
|
// Perform the translation
|
|
225
|
-
const result = await this.doTranslate(conceptMaps, coding, targetScope, targetSystem, txp, reverse
|
|
223
|
+
const result = await this.doTranslate(conceptMaps, coding, targetScope, targetSystem, txp, reverse);
|
|
226
224
|
return res.status(200).json(result);
|
|
227
225
|
}
|
|
228
226
|
|
|
@@ -322,7 +320,7 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
322
320
|
return result;
|
|
323
321
|
}
|
|
324
322
|
|
|
325
|
-
translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, output
|
|
323
|
+
translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, output) {
|
|
326
324
|
let result = false;
|
|
327
325
|
const matches = cm.listTranslations(coding, targetScope, targetSystem);
|
|
328
326
|
if (matches.length > 0) {
|
|
@@ -385,12 +383,10 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
385
383
|
part: productParts
|
|
386
384
|
});
|
|
387
385
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
});
|
|
393
|
-
}
|
|
386
|
+
matchParts.push({
|
|
387
|
+
name: 'originMap',
|
|
388
|
+
valueCanonical: cm.vurl
|
|
389
|
+
});
|
|
394
390
|
output.push({
|
|
395
391
|
name: 'match',
|
|
396
392
|
part: matchParts
|
|
@@ -403,7 +399,7 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
403
399
|
return result;
|
|
404
400
|
}
|
|
405
401
|
|
|
406
|
-
translateUsingGroupsReverse(cm, coding, targetScope, targetSystem, params, output
|
|
402
|
+
translateUsingGroupsReverse(cm, coding, targetScope, targetSystem, params, output) {
|
|
407
403
|
let result = false;
|
|
408
404
|
const matches = cm.listTranslationsReverse(coding, targetScope, targetSystem);
|
|
409
405
|
if (matches.length > 0) {
|
|
@@ -474,12 +470,10 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
474
470
|
part: productParts
|
|
475
471
|
});
|
|
476
472
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
});
|
|
482
|
-
}
|
|
473
|
+
matchParts.push({
|
|
474
|
+
name: 'originMap',
|
|
475
|
+
valueCanonical: cm.vurl
|
|
476
|
+
});
|
|
483
477
|
output.push({
|
|
484
478
|
name: 'match',
|
|
485
479
|
part: matchParts
|
|
@@ -491,10 +485,11 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
491
485
|
return result;
|
|
492
486
|
}
|
|
493
487
|
|
|
494
|
-
async translateUsingCodeSystem(cm, coding, target, params, output, reverse
|
|
488
|
+
async translateUsingCodeSystem(cm, coding, target, params, output, reverse) {
|
|
495
489
|
let result = false;
|
|
496
490
|
const factory = cm.jsonObj.internalSource;
|
|
497
491
|
let prov = await factory.build(this.opContext, []);
|
|
492
|
+
this.opContext.registerProvider(prov);
|
|
498
493
|
|
|
499
494
|
output.push({
|
|
500
495
|
name: 'used-system',
|
|
@@ -536,12 +531,10 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
536
531
|
valueString: t.message
|
|
537
532
|
});
|
|
538
533
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
});
|
|
544
|
-
}
|
|
534
|
+
matchParts.push({
|
|
535
|
+
name: 'originMap',
|
|
536
|
+
valueCanonical: cm.vurl
|
|
537
|
+
});
|
|
545
538
|
output.push({
|
|
546
539
|
name: 'match',
|
|
547
540
|
part: matchParts
|
|
@@ -559,10 +552,9 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
559
552
|
* @param {string} targetSystem - Target code system (optional)
|
|
560
553
|
* @param {Parameters} params - Full parameters object
|
|
561
554
|
* @param {boolean} reverse - Full parameters object*
|
|
562
|
-
* @param {boolean} explicit - If the concept map was named explicitly
|
|
563
555
|
* @returns {Object} Parameters resource with translate result
|
|
564
556
|
*/
|
|
565
|
-
async doTranslate(conceptMaps, coding, targetScope, targetSystem, params, reverse
|
|
557
|
+
async doTranslate(conceptMaps, coding, targetScope, targetSystem, params, reverse) {
|
|
566
558
|
this.deadCheck('doTranslate');
|
|
567
559
|
|
|
568
560
|
const result = [];
|
|
@@ -571,11 +563,11 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
571
563
|
let added = false;
|
|
572
564
|
for (const cm of conceptMaps) {
|
|
573
565
|
if (cm.jsonObj.internalSource) {
|
|
574
|
-
added = await this.translateUsingCodeSystem(cm, coding, targetSystem, params, result, reverse
|
|
566
|
+
added = await this.translateUsingCodeSystem(cm, coding, targetSystem, params, result, reverse) || added;
|
|
575
567
|
} else if (reverse) {
|
|
576
|
-
added = this.translateUsingGroupsReverse(cm, coding, targetScope, targetSystem, params, result
|
|
568
|
+
added = this.translateUsingGroupsReverse(cm, coding, targetScope, targetSystem, params, result) || added;
|
|
577
569
|
} else{
|
|
578
|
-
added = this.translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, result
|
|
570
|
+
added = this.translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, result) || added;
|
|
579
571
|
}
|
|
580
572
|
}
|
|
581
573
|
result.push({
|
package/tx/workers/validate.js
CHANGED
|
@@ -124,7 +124,7 @@ class ValueSetChecker {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
} catch (error) {
|
|
127
|
-
this.log.error(error);
|
|
127
|
+
this.worker.log.error(error);
|
|
128
128
|
debugLog(error);
|
|
129
129
|
throw new Error('Exception expanding value set in order to infer system: ' + error.message);
|
|
130
130
|
}
|
|
@@ -467,7 +467,7 @@ class ValueSetChecker {
|
|
|
467
467
|
return false;
|
|
468
468
|
}
|
|
469
469
|
let cs = await this.worker.findCodeSystem(system, version, this.params, ['complete', 'fragment'], op,true, false, false, this.worker.requiredSupplements);
|
|
470
|
-
this.seeSourceProvider(cs, system);
|
|
470
|
+
this.worker.seeSourceProvider(cs, system);
|
|
471
471
|
if (cs === null) {
|
|
472
472
|
this.worker.opContext.addNote(this.valueSet, 'Didn\'t find CodeSystem "' + this.worker.renderer.displayCoded(system, version) + '"', this.indentCount);
|
|
473
473
|
result = null;
|
|
@@ -563,7 +563,7 @@ class ValueSetChecker {
|
|
|
563
563
|
}
|
|
564
564
|
let msg = await cs.incompleteValidationMessage(ctxt.context, this.params.HTTPLanguages);
|
|
565
565
|
if (msg) {
|
|
566
|
-
op.
|
|
566
|
+
op.addIssue(new Issue('information', 'informational', addToPath(path, 'code'), null, msg, 'process-note'));
|
|
567
567
|
}
|
|
568
568
|
inactive.value = await cs.isInactive(ctxt.context);
|
|
569
569
|
inactive.path = path;
|
|
@@ -824,7 +824,7 @@ class ValueSetChecker {
|
|
|
824
824
|
} else {
|
|
825
825
|
let msg = 'The code system "' + ccc.system + '" version "' + ccc.version + '" in the ValueSet expansion is different to the one in the value ("' + version + '")';
|
|
826
826
|
messages.push(msg);
|
|
827
|
-
op.
|
|
827
|
+
op.addIssue(new Issue('error', 'not-found', addToPath(path, 'version'), null, msg, 'vs-invalid'));
|
|
828
828
|
return false;
|
|
829
829
|
}
|
|
830
830
|
let cs = await this.worker.findCodeSystem(system, v, this.params, ['complete', 'fragment'], op, true, true, false, this.worker.requiredSupplements);
|
|
@@ -1122,7 +1122,7 @@ class ValueSetChecker {
|
|
|
1122
1122
|
if ((cause.value === 'not-found' && contentMode.value !== 'complete') || contentMode.value === 'example') {
|
|
1123
1123
|
let m = 'The system ' + c.system + ' was found but did not contain enough information to properly validate the code "' + c.code + '" ("' + c.display + '") (mode = ' + contentMode.value + ')';
|
|
1124
1124
|
msg(m);
|
|
1125
|
-
op.
|
|
1125
|
+
op.addIssue(new Issue('warning', 'not-found', path, null, m, 'vs-invalid'));
|
|
1126
1126
|
} else if (c.display && list.designations.length > 0) {
|
|
1127
1127
|
await this.checkDisplays(list, defLang, c, msg, op, path);
|
|
1128
1128
|
}
|
|
@@ -1350,14 +1350,18 @@ class ValueSetChecker {
|
|
|
1350
1350
|
if (vstatus.value && vstatus.value !== 'inactive') {
|
|
1351
1351
|
result.addParamCode('status', vstatus.value);
|
|
1352
1352
|
}
|
|
1353
|
+
let mpath = inactive.path;
|
|
1354
|
+
if (!mpath) {
|
|
1355
|
+
mpath = 'code';
|
|
1356
|
+
}
|
|
1353
1357
|
if (!['inactive', 'DISCOURAGED'].includes(vstatus.value)) {
|
|
1354
1358
|
let m = this.worker.i18n.translate('INACTIVE_CONCEPT_FOUND', this.params.HTTPLanguages, ['inactive', tcode]);
|
|
1355
1359
|
msg(m);
|
|
1356
|
-
op.addIssue(new Issue('warning', 'business-rule',
|
|
1360
|
+
op.addIssue(new Issue('warning', 'business-rule', mpath, 'INACTIVE_CONCEPT_FOUND', m, 'code-comment'));
|
|
1357
1361
|
}
|
|
1358
1362
|
let m = this.worker.i18n.translate('INACTIVE_CONCEPT_FOUND', this.params.HTTPLanguages, [vstatus.value, tcode]);
|
|
1359
1363
|
msg(m);
|
|
1360
|
-
op.addIssue(new Issue('warning', 'business-rule',
|
|
1364
|
+
op.addIssue(new Issue('warning', 'business-rule', mpath, 'INACTIVE_CONCEPT_FOUND', m, 'code-comment'));
|
|
1361
1365
|
} else if (vstatus.value && vstatus.value.toLowerCase() === 'deprecated') {
|
|
1362
1366
|
result.addParamCode('status', 'deprecated');
|
|
1363
1367
|
let m = this.worker.i18n.translate('DEPRECATED_CONCEPT_FOUND', this.params.HTTPLanguages, [vstatus.value, tcode]);
|
|
@@ -1414,7 +1418,11 @@ class ValueSetChecker {
|
|
|
1414
1418
|
m = this.worker.i18n.translate('NO_VALID_DISPLAY_AT_ALL', this.params.HTTPLanguages, [c.display, c.system, c.code]);
|
|
1415
1419
|
mid = 'NO_VALID_DISPLAY_AT_ALL';
|
|
1416
1420
|
} else {
|
|
1417
|
-
|
|
1421
|
+
// no displays in the requested language(s): fall back to checking against the
|
|
1422
|
+
// displays + designations in the code system's default language, not just the
|
|
1423
|
+
// single preferred display (see tests validation-simple-*-good-language-none)
|
|
1424
|
+
let hdDef = list.hasDisplay(this.params.workingLanguages(), defLang.value, c.display, false, DisplayCheckingStyle.CASE_INSENSITIVE);
|
|
1425
|
+
if (hdDef.found) {
|
|
1418
1426
|
m = this.worker.i18n.translate('NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_OK', this.params.HTTPLanguages, [c.display, c.system, c.code, this.params.langSummary(), ds]);
|
|
1419
1427
|
mid = 'NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_OK';
|
|
1420
1428
|
severity = 'information';
|
|
@@ -1518,14 +1526,14 @@ class ValueSetChecker {
|
|
|
1518
1526
|
} else if (ok === null) {
|
|
1519
1527
|
result.AddParamBool('result', false);
|
|
1520
1528
|
result.addParamStr('message', 'The system "' + system + '" is unknown so the /"' + code + '" cannot be confirmed to be in the value set ' + this.valueSet.name);
|
|
1521
|
-
op.
|
|
1529
|
+
op.addIssue(new Issue('error', cause.value, 'code', null, 'The system "' + system + '" is unknown so the /"' + code + '" cannot be confirmed to be in the value set ' + this.valueSet.name, 'not-found'));
|
|
1522
1530
|
for (let us of unknownSystems) {
|
|
1523
1531
|
result.addParamCanonical('x-caused-by-unknown-system', us);
|
|
1524
1532
|
}
|
|
1525
1533
|
} else {
|
|
1526
1534
|
result.AddParamBool('result', false);
|
|
1527
1535
|
result.addParamStr('message', 'The system/code "' + system + '"/"' + code + '" is not in the value set ' + this.valueSet.name);
|
|
1528
|
-
op.
|
|
1536
|
+
op.addIssue(new Issue('error', cause.value, 'code', null, 'The system/code "' + system + '"/"' + code + '" is not in the value set ' + this.valueSet.name, 'not-in-vs'));
|
|
1529
1537
|
if (cause.value) {
|
|
1530
1538
|
result.AddParamCode('cause', cause.value);
|
|
1531
1539
|
}
|
package/tx/workers/worker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { TerminologyError} = require('../
|
|
1
|
+
const { TerminologyError} = require('../library/errors');
|
|
2
2
|
const { CodeSystem } = require('../library/codesystem');
|
|
3
3
|
const ValueSet = require('../library/valueset');
|
|
4
4
|
const {VersionUtilities} = require("../../library/version-utilities");
|
|
@@ -184,6 +184,10 @@ class TerminologyWorker {
|
|
|
184
184
|
if (checkVer) {
|
|
185
185
|
this.checkVersion(url, provider.version(), params, provider.versionAlgorithm(), op);
|
|
186
186
|
}
|
|
187
|
+
// Track for lifecycle cleanup at end of request. Providers built from
|
|
188
|
+
// a factory open a fresh sqlite connection that needs releasing;
|
|
189
|
+
// resource-built providers and others without close() are no-ops.
|
|
190
|
+
this.opContext.registerProvider(provider);
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
return provider;
|
package/tx/xversion/xv-bundle.js
CHANGED
|
@@ -139,9 +139,12 @@ function codeSystemR5ToR3(r5Obj) {
|
|
|
139
139
|
* @private
|
|
140
140
|
*/
|
|
141
141
|
function isR5OnlyFilterOperator(operator) {
|
|
142
|
+
// Operators added in R5 that are not valid in R4 or earlier.
|
|
143
|
+
// NOTE: 'generalizes' is NOT R5-only — it is a valid R4 operator
|
|
144
|
+
// (filter-operator value set, 4.0.1), so it must not be stripped here.
|
|
142
145
|
const r5OnlyOperators = [
|
|
143
|
-
'
|
|
144
|
-
//
|
|
146
|
+
'child-of', // Added in R5
|
|
147
|
+
'descendent-leaf', // Added in R5
|
|
145
148
|
];
|
|
146
149
|
return r5OnlyOperators.includes(operator);
|
|
147
150
|
}
|
|
@@ -18,7 +18,7 @@ function parametersToR5(jsonObj, sourceVersion) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const {convertResourceToR5} = require("./xv-resource");
|
|
21
|
-
for (let p of jsonObj.parameter) {
|
|
21
|
+
for (let p of jsonObj.parameter || []) {
|
|
22
22
|
if (p.resource) {
|
|
23
23
|
p.resource = convertResourceToR5(p.resource, sourceVersion);
|
|
24
24
|
}
|
|
@@ -59,7 +59,7 @@ function parametersFromR5(r5Obj, targetVersion) {
|
|
|
59
59
|
function parametersR5ToR4(r5Obj) {
|
|
60
60
|
const {convertResourceFromR5} = require("./xv-resource");
|
|
61
61
|
|
|
62
|
-
for (let p of r5Obj.parameter) {
|
|
62
|
+
for (let p of r5Obj.parameter || []) {
|
|
63
63
|
if (p.resource) {
|
|
64
64
|
p.resource = convertResourceFromR5(p.resource, "R4");
|
|
65
65
|
}
|
|
@@ -71,7 +71,7 @@ function parametersR5ToR4(r5Obj) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function convertResourceWithinR5(r5Obj) {
|
|
74
|
-
for (let p of r5Obj.parameter) {
|
|
74
|
+
for (let p of r5Obj.parameter || []) {
|
|
75
75
|
if (p.name == 'match') {
|
|
76
76
|
fixMatchParameterfor5(p);
|
|
77
77
|
}
|
|
@@ -135,7 +135,7 @@ function convertParameterR5ToR3(p) {
|
|
|
135
135
|
function parametersR5ToR3(r5Obj) {
|
|
136
136
|
const {convertResourceFromR5} = require("./xv-resource");
|
|
137
137
|
|
|
138
|
-
for (let p of r5Obj.parameter) {
|
|
138
|
+
for (let p of r5Obj.parameter || []) {
|
|
139
139
|
if (p.resource) {
|
|
140
140
|
p.resource = convertResourceFromR5(p.resource, "R3");
|
|
141
141
|
}
|
|
@@ -9,7 +9,7 @@ const {bundleFromR5, bundleToR5} = require("./xv-bundle");
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
function convertResourceToR5(data, sourceVersion) {
|
|
12
|
-
if (sourceVersion == "5.0" || !data.resourceType) {
|
|
12
|
+
if (!data || sourceVersion == "5.0" || !data.resourceType) {
|
|
13
13
|
return data;
|
|
14
14
|
}
|
|
15
15
|
switch (data.resourceType) {
|
|
@@ -26,7 +26,7 @@ function convertResourceToR5(data, sourceVersion) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function convertResourceFromR5(data, targetVersion) {
|
|
29
|
-
if (targetVersion == "5.0" || !data.resourceType) {
|
|
29
|
+
if (!data || targetVersion == "5.0" || !data.resourceType) {
|
|
30
30
|
return data;
|
|
31
31
|
}
|
|
32
32
|
switch (data.resourceType) {
|
|
@@ -15,17 +15,22 @@ function terminologyCapabilitiesToR5(jsonObj, sourceVersion) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
if (VersionUtilities.isR4Ver(sourceVersion)) {
|
|
18
|
+
const contentExtUrl = "http://hl7.org/fhir/5.0/StructureDefinition/extension-TerminologyCapabilities.codeSystem.content";
|
|
18
19
|
for (const cs of jsonObj.codeSystem || []) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
// In R4 the content is carried only by the content extension (there is no
|
|
21
|
+
// native codeSystem.content), so read it from the extension directly.
|
|
22
|
+
const cnt = Extensions.readString(cs, contentExtUrl);
|
|
23
|
+
if (cnt) {
|
|
24
|
+
cs.content = cnt;
|
|
25
|
+
// Remove the now-redundant source extension (the field is `extension`,
|
|
26
|
+
// singular). Filter so any unrelated extensions are preserved.
|
|
27
|
+
cs.extension = (cs.extension || []).filter(e => e.url !== contentExtUrl);
|
|
28
|
+
if (cs.extension.length === 0) {
|
|
29
|
+
delete cs.extension;
|
|
24
30
|
}
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
|
|
29
34
|
return jsonObj;
|
|
30
35
|
}
|
|
31
36
|
|