fhirsmith 0.14.0 → 0.14.1
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 +29 -0
- package/config-template.json +11 -0
- package/library/languages.js +15 -0
- package/library/same-origin.js +62 -0
- package/nginx.md +17 -9
- package/package.json +1 -1
- package/publisher/publisher-template.html +1 -0
- package/publisher/publisher.js +95 -42
- package/registry/registry.js +3 -1
- package/server.js +6 -1
- package/testing/readme.md +32 -0
- package/testing/render.js +270 -29
- package/testing/store.js +152 -8
- package/testing/testing.js +283 -9
- package/tx/cs/cs-snomed.js +3 -72
- package/tx/sct/editions.js +70 -0
- package/tx/workers/expand.js +2 -2
- package/tx/workers/translate.js +25 -1
- package/tx/workers/validate.js +9 -5
- package/tx/workers/worker.js +3 -35
- package/utilities/submit-test-report.js +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SNOMED CT editions FHIRsmith knows by name - the one table, used for the provider's
|
|
3
|
+
* description and name (cs-snomed.js) and for the edition named in unknown-code messages
|
|
4
|
+
* (SCTVersion in workers/worker.js). Keyed by the edition's module id, as it appears in a
|
|
5
|
+
* version URI: http://snomed.info/sct/[module]/version/[date]
|
|
6
|
+
*
|
|
7
|
+
* name - human readable edition name
|
|
8
|
+
* code - short code used in the provider name ("SCT US")
|
|
9
|
+
*
|
|
10
|
+
* (The importer and the registry keep their own tables: those carry import settings and
|
|
11
|
+
* registry codes, not display names.)
|
|
12
|
+
*/
|
|
13
|
+
const EDITIONS = {
|
|
14
|
+
'900000000000207008': { name: 'International Edition', code: 'Intl' },
|
|
15
|
+
// The terminology-ecosystem test distribution, published under xsct - see the
|
|
16
|
+
// tx-ecosystem IG, which requires a server claiming the snomed mode to load it.
|
|
17
|
+
'31000003106': { name: 'Test Edition', code: 'Test' },
|
|
18
|
+
'449081005': { name: 'International Spanish Edition', code: 'es' },
|
|
19
|
+
'11000221109': { name: 'Argentinian Edition', code: 'AR-es' },
|
|
20
|
+
'32506021000036107': { name: 'Australian Edition (with drug extension)', code: 'AU+' },
|
|
21
|
+
'11000234105': { name: 'Austrian Edition', code: 'AT' },
|
|
22
|
+
'11000172109': { name: 'Belgian Edition', code: 'BE' },
|
|
23
|
+
'20621000087109': { name: 'Canadian English Edition', code: 'CA-en' },
|
|
24
|
+
'20611000087101': { name: 'Canadian Canadian French Edition', code: 'CA-fr' },
|
|
25
|
+
'554471000005108': { name: 'Danish Edition', code: 'DK' },
|
|
26
|
+
'11000279109': { name: 'Czech Edition', code: 'CZ' },
|
|
27
|
+
'11000181102': { name: 'Estonian Edition', code: 'ET' },
|
|
28
|
+
'11000229106': { name: 'Finnish Edition', code: 'FI' },
|
|
29
|
+
'11000274103': { name: 'German Edition', code: 'DE' },
|
|
30
|
+
'1121000189102': { name: 'Indian Edition', code: 'IN' },
|
|
31
|
+
'827022005': { name: 'IPS Terminology', code: 'IPS' },
|
|
32
|
+
'11000220105': { name: 'Irish Edition', code: 'IE' },
|
|
33
|
+
'11000146104': { name: 'Netherlands Edition', code: 'NL' },
|
|
34
|
+
'21000210109': { name: 'New Zealand Edition', code: 'NZ' },
|
|
35
|
+
'51000202101': { name: 'Norwegian Edition', code: 'NO' },
|
|
36
|
+
'11000267109': { name: 'Republic of Korea Edition (South Korea)', code: 'KR' },
|
|
37
|
+
'900000001000122104': { name: 'Spanish National Edition', code: 'ES-es' },
|
|
38
|
+
'45991000052106': { name: 'Swedish Edition', code: 'SE' },
|
|
39
|
+
'2011000195101': { name: 'Swiss Edition', code: 'CH' },
|
|
40
|
+
'83821000000107': { name: 'UK Edition', code: 'UK' },
|
|
41
|
+
'999000021000000109': { name: 'UK Clinical Edition', code: 'UK-Clinical' },
|
|
42
|
+
'5631000179106': { name: 'Uruguay Edition', code: 'UY' },
|
|
43
|
+
'731000124108': { name: 'US Edition', code: 'US' },
|
|
44
|
+
'21000325107': { name: 'Chilean Edition', code: 'CL' },
|
|
45
|
+
'5991000124107': { name: 'US Edition (with ICD-10-CM maps)', code: 'US+' }
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} edition - edition module id
|
|
50
|
+
* @returns {string|null} the edition's name, or null if it isn't a known edition
|
|
51
|
+
*/
|
|
52
|
+
function editionName(edition) {
|
|
53
|
+
const e = EDITIONS[edition];
|
|
54
|
+
return e ? e.name : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {string} edition - edition module id
|
|
59
|
+
* @returns {string|null} the edition's short code, or null if it isn't a known edition
|
|
60
|
+
*/
|
|
61
|
+
function editionCode(edition) {
|
|
62
|
+
const e = EDITIONS[edition];
|
|
63
|
+
return e ? e.code : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
EDITIONS,
|
|
68
|
+
editionName,
|
|
69
|
+
editionCode
|
|
70
|
+
};
|
package/tx/workers/expand.js
CHANGED
|
@@ -1385,9 +1385,9 @@ class ValueSetExpander {
|
|
|
1385
1385
|
}
|
|
1386
1386
|
|
|
1387
1387
|
if (this.params.DisplayLanguages) {
|
|
1388
|
-
this.addParamCode(exp, 'displayLanguage', this.params.DisplayLanguages.
|
|
1388
|
+
this.addParamCode(exp, 'displayLanguage', this.params.DisplayLanguages.asParameter());
|
|
1389
1389
|
} else if (this.params.HTTPLanguages) {
|
|
1390
|
-
this.addParamCode(exp, 'displayLanguage', this.params.HTTPLanguages.
|
|
1390
|
+
this.addParamCode(exp, 'displayLanguage', this.params.HTTPLanguages.asParameter());
|
|
1391
1391
|
}
|
|
1392
1392
|
if (this.params.designations) {
|
|
1393
1393
|
for (const s of this.params.designations) {
|
package/tx/workers/translate.js
CHANGED
|
@@ -1085,9 +1085,14 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
1085
1085
|
added = await this.translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, result) || added;
|
|
1086
1086
|
}
|
|
1087
1087
|
}
|
|
1088
|
+
// A map can say, explicitly, that a concept is NOT related to a target (not-related-to,
|
|
1089
|
+
// or R4's unmatched/disjoint). That is still reported as a match - it is what the map
|
|
1090
|
+
// says - but it is not a translation: "The value can only be true if at least one
|
|
1091
|
+
// returned match has a relationship other than 'not-related-to'" (tx-ecosystem translate-2b)
|
|
1092
|
+
const translated = added && result.some(p => p.name === 'match' && this.isPositiveMatch(p));
|
|
1088
1093
|
result.push({
|
|
1089
1094
|
name: 'result',
|
|
1090
|
-
valueBoolean:
|
|
1095
|
+
valueBoolean: translated
|
|
1091
1096
|
});
|
|
1092
1097
|
if (!added) {
|
|
1093
1098
|
result.push({
|
|
@@ -1120,6 +1125,25 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
1120
1125
|
};
|
|
1121
1126
|
}
|
|
1122
1127
|
|
|
1128
|
+
/**
|
|
1129
|
+
* Whether a match counts towards result=true. A noMap match says there is no mapping, and
|
|
1130
|
+
* an explicit negative relationship says the target is not related, so neither is a
|
|
1131
|
+
* translation. A match with no relationship otherwise (an unmapped fallback without one,
|
|
1132
|
+
* a code system supplied translation) still counts.
|
|
1133
|
+
*/
|
|
1134
|
+
isPositiveMatch(match) {
|
|
1135
|
+
const part = (name) => (match.part || []).find(p => p.name === name);
|
|
1136
|
+
if (part('noMap')?.valueBoolean === true) {
|
|
1137
|
+
return false;
|
|
1138
|
+
}
|
|
1139
|
+
const rel = part('relationship')?.valueCode;
|
|
1140
|
+
const eq = part('equivalence')?.valueCode;
|
|
1141
|
+
if (eq === 'unmatched' || eq === 'disjoint') {
|
|
1142
|
+
return false;
|
|
1143
|
+
}
|
|
1144
|
+
return rel !== 'not-related-to';
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1123
1147
|
// eslint-disable-next-line no-unused-vars
|
|
1124
1148
|
isOkTarget(cm, vs) {
|
|
1125
1149
|
// if cm.target != null then
|
package/tx/workers/validate.js
CHANGED
|
@@ -1088,7 +1088,11 @@ class ValueSetChecker {
|
|
|
1088
1088
|
mt.push(m);
|
|
1089
1089
|
op.addIssue(new Issue('error', 'invalid', p, 'Terminology_TX_System_Relative', m, 'invalid-data'));
|
|
1090
1090
|
}
|
|
1091
|
-
|
|
1091
|
+
// the code isn't in the value set, but it's still looked up in the code system to report on
|
|
1092
|
+
// it - and that has to be the version the value set selects when the coding doesn't say,
|
|
1093
|
+
// not the server default (tx-ecosystem sct validate-code-pc-none)
|
|
1094
|
+
let lookupVersion = c.version || vsImpliedVersion;
|
|
1095
|
+
let prov = await this.worker.findCodeSystem(ws, lookupVersion, this.params, ['complete', 'fragment'], op,true, true, false, this.worker.requiredSupplements);
|
|
1092
1096
|
if (prov === null) {
|
|
1093
1097
|
let vss = await this.worker.findValueSet(ws, '', null);
|
|
1094
1098
|
if (vss !== null) {
|
|
@@ -1098,7 +1102,7 @@ class ValueSetChecker {
|
|
|
1098
1102
|
op.addIssue(new Issue('error', 'invalid', addToPath(path, 'system'), 'Terminology_TX_System_ValueSet2', m, 'invalid-data'));
|
|
1099
1103
|
cause.value = 'invalid';
|
|
1100
1104
|
} else {
|
|
1101
|
-
let provS = await this.worker.findCodeSystem(ws,
|
|
1105
|
+
let provS = await this.worker.findCodeSystem(ws, lookupVersion, this.params, ['supplement'], op,true, true, false, this.worker.requiredSupplements);
|
|
1102
1106
|
if (provS !== null) {
|
|
1103
1107
|
vss = null;
|
|
1104
1108
|
let m = this.worker.i18n.translate('CODESYSTEM_CS_NO_SUPPLEMENT', this.params.HTTPLanguages, [provS.vurl()]);
|
|
@@ -1109,7 +1113,7 @@ class ValueSetChecker {
|
|
|
1109
1113
|
let prov2 = await this.worker.findCodeSystem(ws, '', this.params, ['complete', 'fragment'], op,true, true, false, this.worker.requiredSupplements);
|
|
1110
1114
|
let bAdd = true;
|
|
1111
1115
|
let m, mid, vn;
|
|
1112
|
-
if (prov2 === null && !
|
|
1116
|
+
if (prov2 === null && !lookupVersion) {
|
|
1113
1117
|
mid = 'UNKNOWN_CODESYSTEM';
|
|
1114
1118
|
m = this.worker.i18n.translate('UNKNOWN_CODESYSTEM', this.params.HTTPLanguages, [ws]);
|
|
1115
1119
|
bAdd = !unknownSystems.has(ws);
|
|
@@ -1123,9 +1127,9 @@ class ValueSetChecker {
|
|
|
1123
1127
|
vn = ws;
|
|
1124
1128
|
} else {
|
|
1125
1129
|
mid = 'UNKNOWN_CODESYSTEM_VERSION';
|
|
1126
|
-
vn = ws + '|' +
|
|
1130
|
+
vn = ws + '|' + lookupVersion;
|
|
1127
1131
|
}
|
|
1128
|
-
m = this.worker.i18n.translate(mid, this.params.HTTPLanguages, [ws,
|
|
1132
|
+
m = this.worker.i18n.translate(mid, this.params.HTTPLanguages, [ws, lookupVersion, this.worker.presentVersionList(vl)]);
|
|
1129
1133
|
bAdd = !unknownSystems.has(vn);
|
|
1130
1134
|
if (bAdd) {
|
|
1131
1135
|
unknownSystems.add(vn);
|
package/tx/workers/worker.js
CHANGED
|
@@ -7,6 +7,7 @@ const {Issue} = require("../library/operation-outcome");
|
|
|
7
7
|
const {Languages} = require("../../library/languages");
|
|
8
8
|
const {ConceptMap} = require("../library/conceptmap");
|
|
9
9
|
const {Renderer} = require("../library/renderer");
|
|
10
|
+
const {editionName} = require("../sct/editions");
|
|
10
11
|
|
|
11
12
|
// The cache-id travels as an HTTP header (not an operation parameter) so proxies /
|
|
12
13
|
// load-balancers can act on it and the server can reject it before parsing the body.
|
|
@@ -1100,42 +1101,9 @@ function Unknown_Code_in_VersionSCT(url, version) {
|
|
|
1100
1101
|
function SCTVersion(url, ver) {
|
|
1101
1102
|
if (url !== 'http://snomed.info/sct' || !ver) {
|
|
1102
1103
|
return '';
|
|
1103
|
-
} else {
|
|
1104
|
-
let result = 'unknown';
|
|
1105
|
-
let s = ver.split('/');
|
|
1106
|
-
if (s.length >= 5) {
|
|
1107
|
-
if (s[4] === '900000000000207008') result = 'International Edition';
|
|
1108
|
-
else if (s[4] === '449081005') result = 'International Spanish Edition';
|
|
1109
|
-
else if (s[4] === '11000221109') result = 'Argentinian Edition';
|
|
1110
|
-
else if (s[4] === '32506021000036107') result = 'Australian Edition (with drug extension)';
|
|
1111
|
-
else if (s[4] === '11000234105') result = 'Austrian Edition';
|
|
1112
|
-
else if (s[4] === '11000172109') result = 'Belgian Edition';
|
|
1113
|
-
else if (s[4] === '20621000087109') result = 'Canadian English Edition';
|
|
1114
|
-
else if (s[4] === '20611000087101') result = 'Canadian Canadian French Edition';
|
|
1115
|
-
else if (s[4] === '11000279109') result = 'Czech Edition';
|
|
1116
|
-
else if (s[4] === '554471000005108') result = 'Danish Edition';
|
|
1117
|
-
else if (s[4] === '11000181102') result = 'Estonian Edition';
|
|
1118
|
-
else if (s[4] === '11000229106') result = 'Finnish Edition';
|
|
1119
|
-
else if (s[4] === '11000274103') result = 'German Edition';
|
|
1120
|
-
else if (s[4] === '1121000189102') result = 'Indian Edition';
|
|
1121
|
-
else if (s[4] === '827022005') result = 'IPS Terminology';
|
|
1122
|
-
else if (s[4] === '11000220105') result = 'Irish Edition';
|
|
1123
|
-
else if (s[4] === '11000146104') result = 'Netherlands Edition';
|
|
1124
|
-
else if (s[4] === '21000210109') result = 'New Zealand Edition';
|
|
1125
|
-
else if (s[4] === '51000202101') result = 'Norwegian Edition';
|
|
1126
|
-
else if (s[4] === '11000267109') result = 'Republic of Korea Edition (South Korea)';
|
|
1127
|
-
else if (s[4] === '900000001000122104') result = 'Spanish National Edition';
|
|
1128
|
-
else if (s[4] === '45991000052106') result = 'Swedish Edition';
|
|
1129
|
-
else if (s[4] === '2011000195101') result = 'Swiss Edition';
|
|
1130
|
-
else if (s[4] === '83821000000107') result = 'UK Edition';
|
|
1131
|
-
else if (s[4] === '999000021000000109') result = 'UK Clinical Edition';
|
|
1132
|
-
else if (s[4] === '5631000179106') result = 'Uruguay Edition';
|
|
1133
|
-
else if (s[4] === '21000325107') result = 'Chilean Edition';
|
|
1134
|
-
else if (s[4] === '731000124108') result = 'US Edition';
|
|
1135
|
-
else if (s[4] === '5991000124107') result = 'US Edition (with ICD-10-CM maps)';
|
|
1136
|
-
}
|
|
1137
|
-
return result;
|
|
1138
1104
|
}
|
|
1105
|
+
const s = ver.split('/');
|
|
1106
|
+
return (s.length >= 5 && editionName(s[4])) || 'unknown';
|
|
1139
1107
|
}
|
|
1140
1108
|
|
|
1141
1109
|
module.exports = {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
const fs = require('fs');
|
|
23
23
|
const path = require('path');
|
|
24
24
|
|
|
25
|
-
const DEFAULT_SERVER = 'https://
|
|
25
|
+
const DEFAULT_SERVER = 'https://npm.fhir.org/testing';
|
|
26
26
|
const DEFAULT_FILE = path.join(__dirname, '..', 'test-cases-report.json');
|
|
27
27
|
|
|
28
28
|
function usage(msg) {
|