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.
@@ -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
+ };
@@ -1385,9 +1385,9 @@ class ValueSetExpander {
1385
1385
  }
1386
1386
 
1387
1387
  if (this.params.DisplayLanguages) {
1388
- this.addParamCode(exp, 'displayLanguage', this.params.DisplayLanguages.asString(true));
1388
+ this.addParamCode(exp, 'displayLanguage', this.params.DisplayLanguages.asParameter());
1389
1389
  } else if (this.params.HTTPLanguages) {
1390
- this.addParamCode(exp, 'displayLanguage', this.params.HTTPLanguages.asString(true));
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) {
@@ -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: added
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
@@ -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
- let prov = await this.worker.findCodeSystem(ws, c.version, this.params, ['complete', 'fragment'], op,true, true, false, this.worker.requiredSupplements);
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, c.version, this.params, ['supplement'], op,true, true, false, this.worker.requiredSupplements);
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 && !c.version) {
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 + '|' + c.version;
1130
+ vn = ws + '|' + lookupVersion;
1127
1131
  }
1128
- m = this.worker.i18n.translate(mid, this.params.HTTPLanguages, [ws, c.version, this.worker.presentVersionList(vl)]);
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);
@@ -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://testing.fhir.org/testing';
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) {