fhirsmith 0.4.2 → 0.5.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/library/cron-utilities.js +136 -0
  4. package/library/html-server.js +13 -29
  5. package/library/html.js +3 -8
  6. package/library/languages.js +160 -37
  7. package/library/package-manager.js +48 -1
  8. package/library/utilities.js +100 -19
  9. package/package.json +2 -2
  10. package/packages/package-crawler.js +6 -1
  11. package/packages/packages.js +38 -54
  12. package/publisher/publisher.js +19 -27
  13. package/registry/api.js +11 -10
  14. package/registry/crawler.js +31 -29
  15. package/registry/model.js +5 -26
  16. package/registry/registry.js +32 -41
  17. package/server.js +53 -5
  18. package/shl/shl.js +0 -18
  19. package/static/assets/js/statuspage.js +1 -9
  20. package/stats.js +39 -1
  21. package/token/token.js +14 -9
  22. package/translations/Messages.properties +2 -1
  23. package/tx/README.md +17 -6
  24. package/tx/cs/cs-api.js +19 -1
  25. package/tx/cs/cs-base.js +77 -0
  26. package/tx/cs/cs-country.js +46 -0
  27. package/tx/cs/cs-cpt.js +9 -5
  28. package/tx/cs/cs-cs.js +27 -13
  29. package/tx/cs/cs-lang.js +60 -22
  30. package/tx/cs/cs-loinc.js +69 -98
  31. package/tx/cs/cs-mimetypes.js +4 -0
  32. package/tx/cs/cs-ndc.js +6 -0
  33. package/tx/cs/cs-omop.js +16 -15
  34. package/tx/cs/cs-rxnorm.js +23 -1
  35. package/tx/cs/cs-snomed.js +283 -40
  36. package/tx/cs/cs-ucum.js +90 -70
  37. package/tx/importers/import-sct.module.js +371 -35
  38. package/tx/importers/readme.md +117 -7
  39. package/tx/library/bundle.js +5 -0
  40. package/tx/library/capabilitystatement.js +3 -142
  41. package/tx/library/codesystem.js +19 -173
  42. package/tx/library/conceptmap.js +4 -218
  43. package/tx/library/designations.js +14 -1
  44. package/tx/library/extensions.js +7 -0
  45. package/tx/library/namingsystem.js +3 -89
  46. package/tx/library/operation-outcome.js +8 -3
  47. package/tx/library/parameters.js +3 -2
  48. package/tx/library/renderer.js +10 -6
  49. package/tx/library/terminologycapabilities.js +3 -243
  50. package/tx/library/valueset.js +3 -235
  51. package/tx/library.js +100 -13
  52. package/tx/operation-context.js +23 -4
  53. package/tx/params.js +35 -38
  54. package/tx/provider.js +6 -5
  55. package/tx/sct/expressions.js +12 -3
  56. package/tx/tx-html.js +80 -89
  57. package/tx/tx.fhir.org.yml +6 -5
  58. package/tx/tx.js +163 -13
  59. package/tx/vs/vs-database.js +56 -39
  60. package/tx/vs/vs-package.js +21 -2
  61. package/tx/vs/vs-vsac.js +175 -39
  62. package/tx/workers/batch-validate.js +2 -0
  63. package/tx/workers/batch.js +2 -0
  64. package/tx/workers/expand.js +132 -112
  65. package/tx/workers/lookup.js +33 -14
  66. package/tx/workers/metadata.js +2 -2
  67. package/tx/workers/read.js +3 -2
  68. package/tx/workers/related.js +574 -0
  69. package/tx/workers/search.js +46 -9
  70. package/tx/workers/subsumes.js +13 -3
  71. package/tx/workers/translate.js +7 -3
  72. package/tx/workers/validate.js +258 -285
  73. package/tx/workers/worker.js +43 -39
  74. package/tx/xml/bundle-xml.js +237 -0
  75. package/tx/xml/xml-base.js +215 -64
  76. package/tx/xversion/xv-bundle.js +71 -0
  77. package/tx/xversion/xv-capabiliityStatement.js +137 -0
  78. package/tx/xversion/xv-codesystem.js +169 -0
  79. package/tx/xversion/xv-conceptmap.js +224 -0
  80. package/tx/xversion/xv-namingsystem.js +88 -0
  81. package/tx/xversion/xv-operationoutcome.js +27 -0
  82. package/tx/xversion/xv-parameters.js +87 -0
  83. package/tx/xversion/xv-resource.js +45 -0
  84. package/tx/xversion/xv-terminologyCapabilities.js +214 -0
  85. package/tx/xversion/xv-valueset.js +234 -0
  86. package/utilities/dev-proxy-server.js +126 -0
  87. package/utilities/explode-results.js +58 -0
  88. package/utilities/split-by-system.js +198 -0
  89. package/utilities/vsac-cs-fetcher.js +0 -0
  90. package/{windows-install.js → utilities/windows-install.js} +2 -0
  91. package/vcl/vcl.js +0 -18
  92. package/xig/xig.js +108 -99
package/tx/cs/cs-lang.js CHANGED
@@ -89,7 +89,7 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
89
89
  // ========== Code Information Methods ==========
90
90
 
91
91
  async code(code) {
92
-
92
+
93
93
  const ctxt = await this.#ensureContext(code);
94
94
  if (ctxt instanceof Language) {
95
95
  return ctxt.code;
@@ -98,13 +98,26 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
98
98
  }
99
99
 
100
100
  async display(code) {
101
-
101
+
102
102
  const ctxt = await this.#ensureContext(code);
103
103
  if (!ctxt) {
104
104
  return null;
105
105
  }
106
- if (this.opContext.langs.isEnglishOrNothing()) {
107
- return this.languageDefinitions.present(ctxt).trim();
106
+ if (!this.opContext.langs.isEnglishOrNothing()) {
107
+ // Try translated display for the primary requested language
108
+ const primaryLang = this.opContext.langs.getPrimary();
109
+ if (primaryLang && primaryLang.language) {
110
+ const langTranslation = this.languageDefinitions.getTranslatedDisplayForLang(ctxt.language, primaryLang.language);
111
+ if (langTranslation && langTranslation !== ctxt.language) {
112
+ if (ctxt.isLangRegion()) {
113
+ const regionTranslation = this.languageDefinitions.getTranslatedDisplayForRegion(ctxt.region, primaryLang.language);
114
+ if (regionTranslation && regionTranslation !== ctxt.region) {
115
+ return `${langTranslation} (${regionTranslation})`;
116
+ }
117
+ }
118
+ return langTranslation;
119
+ }
120
+ }
108
121
  }
109
122
  let disp = this._displayFromSupplements(ctxt.code);
110
123
  if (disp) {
@@ -114,31 +127,26 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
114
127
  }
115
128
 
116
129
  async definition(code) {
117
-
118
130
  await this.#ensureContext(code);
119
131
  return null; // No definitions for language codes
120
132
  }
121
133
 
122
134
  async isAbstract(code) {
123
-
124
135
  await this.#ensureContext(code);
125
136
  return false; // Language codes are not abstract
126
137
  }
127
138
 
128
139
  async isInactive(code) {
129
-
130
140
  await this.#ensureContext(code);
131
141
  return false; // We don't track inactive language codes
132
142
  }
133
143
 
134
144
  async isDeprecated(code) {
135
-
136
145
  await this.#ensureContext(code);
137
146
  return false; // We don't track deprecated language codes
138
147
  }
139
148
 
140
149
  async designations(code, displays) {
141
-
142
150
  const ctxt = await this.#ensureContext(code);
143
151
  const designations = [];
144
152
  if (ctxt != null) {
@@ -149,8 +157,12 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
149
157
  const regionDisplay = this.languageDefinitions.getDisplayForRegion(ctxt.region);
150
158
  const regionVariant = `${langDisplay} (${regionDisplay})`;
151
159
  const regionVariant2 = `${langDisplay} (Region=${regionDisplay})`;
152
- displays.addDesignation(false, 'active', 'en', CodeSystem.makeUseForDisplay(), regionVariant);
160
+ const regionVariant3 = `${langDisplay}-${regionDisplay}`;
161
+ const regionVariant4 = `${langDisplay}-${regionDisplay.toUpperCase()}`;
153
162
  displays.addDesignation(false, 'active', 'en', CodeSystem.makeUseForDisplay(), regionVariant2);
163
+ displays.addDesignation(false, 'active', 'en', CodeSystem.makeUseForDisplay(), regionVariant3);
164
+ displays.addDesignation(false, 'active', 'en', CodeSystem.makeUseForDisplay(), regionVariant4);
165
+ displays.addDesignation(false, 'active', 'en', CodeSystem.makeUseForDisplay(), regionVariant);
154
166
  }
155
167
  // add alternative displays if available
156
168
  const displayCount = this.languageDefinitions.displayCount(ctxt);
@@ -167,6 +179,32 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
167
179
  }
168
180
  }
169
181
  }
182
+ // add translated designations from CSV data
183
+ const translationLangs = ['fr', 'de', 'es', 'ar', 'zh', 'ru', 'ja', 'sw'];
184
+ // languages that don't have upper/lower case distinction
185
+ const caselessLangs = new Set(['ar', 'zh', 'ja']);
186
+
187
+ for (const tLang of translationLangs) {
188
+ const langTranslation = this.languageDefinitions.getTranslatedDisplayForLang(ctxt.language, tLang);
189
+ if (langTranslation && langTranslation !== ctxt.language) {
190
+ if (ctxt.isLangRegion()) {
191
+ const regionTranslation = this.languageDefinitions.getTranslatedDisplayForRegion(ctxt.region, tLang);
192
+ if (regionTranslation && regionTranslation !== ctxt.region) {
193
+ const translatedDisplay = `${langTranslation} (${regionTranslation})`;
194
+ displays.addDesignation(false, 'active', tLang, CodeSystem.makeUseForDisplay(), translatedDisplay);
195
+ displays.addDesignation(false, 'active', tLang, CodeSystem.makeUseForDisplay(), `${langTranslation} (Region=${regionTranslation})`);
196
+ displays.addDesignation(false, 'active', tLang, CodeSystem.makeUseForDisplay(), `${langTranslation}-${regionTranslation}`);
197
+ if (!caselessLangs.has(tLang)) {
198
+ displays.addDesignation(false, 'active', tLang, CodeSystem.makeUseForDisplay(), `${langTranslation}-${regionTranslation.toUpperCase()}`);
199
+ }
200
+ } else {
201
+ displays.addDesignation(false, 'active', tLang, CodeSystem.makeUseForDisplay(), langTranslation);
202
+ }
203
+ } else {
204
+ displays.addDesignation(false, 'active', tLang, CodeSystem.makeUseForDisplay(), langTranslation);
205
+ }
206
+ }
207
+ }
170
208
  this._listSupplementDesignations(ctxt.code, displays);
171
209
  }
172
210
  return designations;
@@ -194,7 +232,7 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
194
232
  // ========== Lookup Methods ==========
195
233
 
196
234
  async locate(code) {
197
-
235
+
198
236
  assert(!code || typeof code === 'string', 'code must be string');
199
237
  if (!code) return { context: null, message: 'Empty code' };
200
238
 
@@ -209,7 +247,7 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
209
247
  // ========== Filter Methods ==========
210
248
 
211
249
  async doesFilter(prop, op, value) {
212
-
250
+
213
251
  assert(prop != null && typeof prop === 'string', 'prop must be a non-null string');
214
252
  assert(op != null && typeof op === 'string', 'op must be a non-null string');
215
253
  assert(value != null && typeof value === 'string', 'value must be a non-null string');
@@ -222,7 +260,7 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
222
260
  }
223
261
 
224
262
  async searchFilter(filterContext, filter, sort) {
225
-
263
+
226
264
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
227
265
  assert(filter && typeof filter === 'string', 'filter must be a non-null string');
228
266
  assert(typeof sort === 'boolean', 'sort must be a boolean');
@@ -232,7 +270,7 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
232
270
 
233
271
 
234
272
  async filter(filterContext, prop, op, value) {
235
-
273
+
236
274
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
237
275
  assert(prop != null && typeof prop === 'string', 'prop must be a non-null string');
238
276
  assert(op != null && typeof op === 'string', 'op must be a non-null string');
@@ -258,13 +296,13 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
258
296
  }
259
297
 
260
298
  async executeFilters(filterContext) {
261
-
299
+
262
300
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
263
301
  return filterContext.filters;
264
302
  }
265
303
 
266
304
  async filterSize(filterContext, set) {
267
-
305
+
268
306
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
269
307
  assert(set && set instanceof IETFLanguageCodeFilter, 'set must be a IETFLanguageCodeFilter');
270
308
 
@@ -272,27 +310,27 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
272
310
  }
273
311
 
274
312
  async filtersNotClosed(filterContext) {
275
-
313
+
276
314
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
277
315
  return true; // Grammar-based system is not closed
278
316
  }
279
317
 
280
318
  async filterMore(filterContext, set) {
281
-
319
+
282
320
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
283
321
  assert(set && set instanceof IETFLanguageCodeFilter, 'set must be a IETFLanguageCodeFilter');
284
322
  throw new Error('Language valuesets cannot be expanded as they are based on a grammar');
285
323
  }
286
324
 
287
325
  async filterConcept(filterContext, set) {
288
-
326
+
289
327
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
290
328
  assert(set && set instanceof IETFLanguageCodeFilter, 'set must be a IETFLanguageCodeFilter');
291
329
  throw new Error('Language valuesets cannot be expanded as they are based on a grammar');
292
330
  }
293
331
 
294
332
  async filterLocate(filterContext, set, code) {
295
-
333
+
296
334
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
297
335
  assert(set && set instanceof IETFLanguageCodeFilter, 'set must be a IETFLanguageCodeFilter');
298
336
  assert(typeof code === 'string', 'code must be non-null string');
@@ -341,7 +379,7 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
341
379
  }
342
380
 
343
381
  async filterCheck(filterContext, set, concept) {
344
-
382
+
345
383
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
346
384
  assert(set && set instanceof IETFLanguageCodeFilter, 'set must be a IETFLanguageCodeFilter');
347
385
  const ctxt = await this.#ensureContext(concept);
@@ -386,7 +424,7 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
386
424
  // ========== Additional Methods ==========
387
425
 
388
426
  async sameConcept(a, b) {
389
-
427
+
390
428
  const codeA = await this.code(a);
391
429
  const codeB = await this.code(b);
392
430
  return codeA === codeB;
package/tx/cs/cs-loinc.js CHANGED
@@ -2,8 +2,9 @@ const sqlite3 = require('sqlite3').verbose();
2
2
  const assert = require('assert');
3
3
  const { CodeSystem } = require('../library/codesystem');
4
4
  const { Language, Languages} = require('../../library/languages');
5
- const { CodeSystemProvider, CodeSystemFactoryProvider} = require('./cs-api');
5
+ const { CodeSystemFactoryProvider} = require('./cs-api');
6
6
  const { validateOptionalParameter, validateArrayParameter} = require("../../library/utilities");
7
+ const {BaseCSServices} = require("./cs-base");
7
8
 
8
9
  // Context kinds matching Pascal enum
9
10
  const LoincProviderContextKind = {
@@ -13,6 +14,17 @@ const LoincProviderContextKind = {
13
14
  ANSWER: 3 // lpckAnswer
14
15
  };
15
16
 
17
+ const classTypes = {
18
+ '1': 'Laboratory class',
19
+ '2': 'Clinical class',
20
+ '3': 'Claims attachments',
21
+ '4': 'Surveys',
22
+ 'Laboratory class' : '1',
23
+ 'Clinical class' : '2',
24
+ 'Claims attachments' : '3',
25
+ 'Surveys' : '4'
26
+ };
27
+
16
28
  class DescriptionCacheEntry {
17
29
  constructor(display, lang, value, dtype) {
18
30
  this.display = display;
@@ -96,7 +108,7 @@ class LoincPrep {
96
108
  }
97
109
  }
98
110
 
99
- class LoincServices extends CodeSystemProvider {
111
+ class LoincServices extends BaseCSServices {
100
112
  constructor(opContext, supplements, db, sharedData) {
101
113
  super(opContext, supplements);
102
114
  this.db = db;
@@ -293,10 +305,10 @@ class LoincServices extends CodeSystemProvider {
293
305
 
294
306
  // Run all property queries in parallel — they're independent reads on the same key
295
307
  await Promise.all([
296
- this.#addRelationshipProperties(ctxt, params),
297
- this.#addConceptProperties(ctxt, params),
298
- this.#addStatusProperty(ctxt, params),
299
- this.#addRelatedNames(ctxt, params)
308
+ this.#addRelationshipProperties(ctxt, props, params),
309
+ this.#addConceptProperties(ctxt, props,params),
310
+ this.#addStatusProperty(ctxt, props,params),
311
+ this.#addRelatedNames(ctxt, props,params)
300
312
  ]);
301
313
  }
302
314
 
@@ -311,7 +323,7 @@ class LoincServices extends CodeSystemProvider {
311
323
  }
312
324
  }
313
325
 
314
- async #addRelationshipProperties(ctxt, params) {
326
+ async #addRelationshipProperties(ctxt, props, params) {
315
327
  return new Promise((resolve, reject) => {
316
328
  const sql = `
317
329
  SELECT RelationshipTypes.Description as Relationship, Codes.Code, Codes.Description as Value
@@ -326,7 +338,9 @@ class LoincServices extends CodeSystemProvider {
326
338
  reject(err);
327
339
  } else {
328
340
  for (const row of rows) {
329
- this.#addCodeProperty(params, 'property', row.Relationship, row.Code);
341
+ if (this._hasProp(props, row.Relationship, true)) {
342
+ this._addCodeProperty(params, 'property', row.Relationship, row.Code);
343
+ }
330
344
  }
331
345
  resolve();
332
346
  }
@@ -334,7 +348,7 @@ class LoincServices extends CodeSystemProvider {
334
348
  });
335
349
  }
336
350
 
337
- async #addConceptProperties(ctxt, params) {
351
+ async #addConceptProperties(ctxt, props, params) {
338
352
  return new Promise((resolve, reject) => {
339
353
  const sql = `
340
354
  SELECT PropertyTypes.Description, PropertyValues.Value
@@ -349,7 +363,14 @@ class LoincServices extends CodeSystemProvider {
349
363
  reject(err);
350
364
  } else {
351
365
  for (const row of rows) {
352
- this.#addStringProperty(params, 'property', row.Description, row.Value);
366
+ if (this._hasProp(props, row.Description, true)) {
367
+ if (row.Description == 'CLASSTYPE') {
368
+ this._addStringProperty(params, 'property', row.Description, classTypes[row.Value])
369
+ .part.push({name: 'description', valueString: row.Value});
370
+ } else {
371
+ this._addStringProperty(params, 'property', row.Description, row.Value);
372
+ }
373
+ }
353
374
  }
354
375
  resolve();
355
376
  }
@@ -357,7 +378,7 @@ class LoincServices extends CodeSystemProvider {
357
378
  });
358
379
  }
359
380
 
360
- async #addStatusProperty(ctxt, params) {
381
+ async #addStatusProperty(ctxt, props, params) {
361
382
  return new Promise((resolve, reject) => {
362
383
  const sql = 'SELECT StatusKey FROM Codes WHERE CodeKey = ? AND StatusKey != 0';
363
384
 
@@ -367,7 +388,9 @@ class LoincServices extends CodeSystemProvider {
367
388
  } else if (row) {
368
389
  const statusDesc = this.statusCodes.get(row.StatusKey.toString());
369
390
  if (row.StatusKey && statusDesc) {
370
- this.#addStringProperty(params, 'property', 'STATUS', statusDesc);
391
+ if (this._hasProp(props, 'STATUS', true)) {
392
+ this._addStringProperty(params, 'property', 'STATUS', statusDesc);
393
+ }
371
394
  }
372
395
  resolve();
373
396
  } else {
@@ -377,14 +400,20 @@ class LoincServices extends CodeSystemProvider {
377
400
  });
378
401
  }
379
402
 
380
- async #addRelatedNames(ctxt, params) {
403
+ async #addRelatedNames(ctxt, props, params) {
381
404
  const loaded = await this.#loadRelatedNames(ctxt);
382
405
  for (let d of loaded) {
383
- this.#addProperty(params, 'property', 'RELATEDNAMES2', d.value, d.lang);
406
+ if (this._hasProp(props, 'RELATEDNAMES2', true)) {
407
+ this._addProperty(params, 'property', 'RELATEDNAMES2', d.value, d.lang);
408
+ }
384
409
  }
385
410
  }
386
411
 
387
- async #addAllDesignations(ctxt, params) {
412
+ async #addAllDesignations(ctxt, props, params) {
413
+ if (!this._hasProp(props, 'designation', true)) {
414
+ return;
415
+ }
416
+
388
417
  return new Promise((resolve, reject) => {
389
418
  const sql = `
390
419
  SELECT Languages.Code as Lang, DescriptionTypes.Description as DType, Descriptions.Value
@@ -400,7 +429,7 @@ class LoincServices extends CodeSystemProvider {
400
429
  reject(err);
401
430
  } else {
402
431
  for (const row of rows) {
403
- this.#addProperty(params, 'designation', row.dtype, row.value, row.lang);
432
+ this._addProperty(params, 'designation', row.dtype, row.value, row.lang);
404
433
  }
405
434
  resolve();
406
435
  }
@@ -408,57 +437,6 @@ class LoincServices extends CodeSystemProvider {
408
437
  });
409
438
  }
410
439
 
411
- #addProperty(params, type, name, value, language = null) {
412
-
413
- const property = {
414
- name: type,
415
- part: [
416
- { name: 'code', valueCode: name },
417
- { name: 'value', valueString: value }
418
- ]
419
- };
420
-
421
- if (language) {
422
- property.part.push({ name: 'language', valueCode: language });
423
- }
424
-
425
- params.push(property);
426
- }
427
-
428
- #addCodeProperty(params, type, name, value, language = null) {
429
-
430
- const property = {
431
- name: type,
432
- part: [
433
- { name: 'code', valueCode: name },
434
- { name: 'value', valueCode: value }
435
- ]
436
- };
437
-
438
- if (language) {
439
- property.part.push({ name: 'language', valueCode: language });
440
- }
441
-
442
- params.push(property);
443
- }
444
-
445
- #addStringProperty(params, type, name, value, language = null) {
446
-
447
- const property = {
448
- name: type,
449
- part: [
450
- { name: 'code', valueCode: name },
451
- { name: 'value', valueString: value }
452
- ]
453
- };
454
-
455
- if (language) {
456
- property.part.push({ name: 'language', valueCode: language });
457
- }
458
-
459
- params.push(property);
460
- }
461
-
462
440
  async #getDisplaysForContext(ctxt, langs) {
463
441
  validateOptionalParameter(langs, "langs", Languages);
464
442
  const displays = [new LoincDisplay('en-US', ctxt.desc)];
@@ -689,11 +667,11 @@ class LoincServices extends CodeSystemProvider {
689
667
 
690
668
  // LIST filter
691
669
  if (prop === 'LIST' && op === '=' && this.codes.has(value)) {
692
- sql = `SELECT TargetKey as Key FROM Relationships
670
+ sql = `SELECT DISTINCT TargetKey as Key FROM Relationships
693
671
  WHERE RelationshipTypeKey = ${this.relationships.get('Answer')}
694
672
  AND SourceKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
695
673
  ORDER BY SourceKey ASC`;
696
- lsql = `SELECT COUNT(TargetKey) FROM Relationships
674
+ lsql = `SELECT COUNT(DISTINCT TargetKey) FROM Relationships
697
675
  WHERE RelationshipTypeKey = ${this.relationships.get('Answer')}
698
676
  AND SourceKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
699
677
  AND TargetKey = `;
@@ -701,16 +679,16 @@ class LoincServices extends CodeSystemProvider {
701
679
  // answers-for filter
702
680
  else if (prop === 'answers-for' && op === '=') {
703
681
  if (value.startsWith('LL')) {
704
- sql = `SELECT TargetKey as Key FROM Relationships
682
+ sql = `SELECT DISTINCT TargetKey as Key FROM Relationships
705
683
  WHERE RelationshipTypeKey = ${this.relationships.get('Answer')}
706
684
  AND SourceKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
707
685
  ORDER BY SourceKey ASC`;
708
- lsql = `SELECT COUNT(TargetKey) FROM Relationships
686
+ lsql = `SELECT COUNT(DISTINCT TargetKey) FROM Relationships
709
687
  WHERE RelationshipTypeKey = ${this.relationships.get('Answer')}
710
688
  AND SourceKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
711
689
  AND TargetKey = `;
712
690
  } else {
713
- sql = `SELECT TargetKey as Key FROM Relationships
691
+ sql = `SELECT DISTINCT TargetKey as Key FROM Relationships
714
692
  WHERE RelationshipTypeKey = ${this.relationships.get('Answer')}
715
693
  AND SourceKey IN (
716
694
  SELECT SourceKey FROM Relationships
@@ -718,7 +696,7 @@ class LoincServices extends CodeSystemProvider {
718
696
  AND TargetKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
719
697
  )
720
698
  ORDER BY SourceKey ASC`;
721
- lsql = `SELECT COUNT(TargetKey) FROM Relationships
699
+ lsql = `SELECT COUNT(DISTINCT TargetKey) FROM Relationships
722
700
  WHERE RelationshipTypeKey = ${this.relationships.get('Answer')}
723
701
  AND SourceKey IN (SELECT SourceKey FROM Relationships
724
702
  WHERE RelationshipTypeKey = ${this.relationships.get('answers-for')}
@@ -729,20 +707,20 @@ class LoincServices extends CodeSystemProvider {
729
707
  // Relationship equal filter
730
708
  else if (this.relationships.has(prop) && op === '=') {
731
709
  if (this.codes.has(value)) {
732
- sql = `SELECT SourceKey as Key FROM Relationships
710
+ sql = `SELECT DISTINCT SourceKey as Key FROM Relationships
733
711
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
734
712
  AND TargetKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
735
713
  ORDER BY SourceKey ASC`;
736
- lsql = `SELECT COUNT(SourceKey) FROM Relationships
714
+ lsql = `SELECT COUNT(DISTINCT SourceKey) FROM Relationships
737
715
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
738
716
  AND TargetKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
739
717
  AND SourceKey = `;
740
718
  } else {
741
- sql = `SELECT SourceKey as Key FROM Relationships
719
+ sql = `SELECT DISTINCT SourceKey as Key FROM Relationships
742
720
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
743
721
  AND TargetKey IN (SELECT CodeKey FROM Codes WHERE Description = '${this.#sqlWrapString(value)}' COLLATE NOCASE)
744
722
  ORDER BY SourceKey ASC`;
745
- lsql = `SELECT COUNT(SourceKey) FROM Relationships
723
+ lsql = `SELECT COUNT(DISTINCT SourceKey) FROM Relationships
746
724
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
747
725
  AND TargetKey IN (SELECT CodeKey FROM Codes WHERE Description = '${this.#sqlWrapString(value)}' COLLATE NOCASE)
748
726
  AND SourceKey = `;
@@ -751,11 +729,11 @@ class LoincServices extends CodeSystemProvider {
751
729
  // Relationship 'in' filter
752
730
  else if (this.relationships.has(prop) && op === 'in') {
753
731
  const codes = this.#commaListOfCodes(value);
754
- sql = `SELECT SourceKey as Key FROM Relationships
732
+ sql = `SELECT DISTINCT SourceKey as Key FROM Relationships
755
733
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
756
734
  AND TargetKey IN (SELECT CodeKey FROM Codes WHERE Code IN (${codes}))
757
735
  ORDER BY SourceKey ASC`;
758
- lsql = `SELECT COUNT(SourceKey) FROM Relationships
736
+ lsql = `SELECT COUNT(DISTINCT SourceKey) FROM Relationships
759
737
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
760
738
  AND TargetKey IN (SELECT CodeKey FROM Codes WHERE Code IN (${codes}))
761
739
  AND SourceKey = `;
@@ -763,20 +741,20 @@ class LoincServices extends CodeSystemProvider {
763
741
  // Relationship 'exists' filter
764
742
  else if (this.relationships.has(prop) && op === 'exists') {
765
743
  if (this.codes.has(value)) {
766
- sql = `SELECT SourceKey as Key FROM Relationships
744
+ sql = `SELECT DISTINCT SourceKey as Key FROM Relationships
767
745
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
768
746
  AND EXISTS (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
769
747
  ORDER BY SourceKey ASC`;
770
- lsql = `SELECT COUNT(SourceKey) FROM Relationships
748
+ lsql = `SELECT COUNT(DISTINCT SourceKey) FROM Relationships
771
749
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
772
750
  AND EXISTS (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}')
773
751
  AND SourceKey = `;
774
752
  } else {
775
- sql = `SELECT SourceKey as Key FROM Relationships
753
+ sql = `SELECT DISTINCT SourceKey as Key FROM Relationships
776
754
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
777
755
  AND EXISTS (SELECT CodeKey FROM Codes WHERE Description = '${this.#sqlWrapString(value)}' COLLATE NOCASE)
778
756
  ORDER BY SourceKey ASC`;
779
- lsql = `SELECT COUNT(SourceKey) FROM Relationships
757
+ lsql = `SELECT COUNT(DISTINCT SourceKey) FROM Relationships
780
758
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
781
759
  AND EXISTS (SELECT CodeKey FROM Codes WHERE Description = '${this.#sqlWrapString(value)}' COLLATE NOCASE)
782
760
  AND SourceKey = `;
@@ -791,11 +769,11 @@ class LoincServices extends CodeSystemProvider {
791
769
  'Description'
792
770
  );
793
771
  if (matchingKeys.length > 0) {
794
- sql = `SELECT SourceKey as Key FROM Relationships
772
+ sql = `SELECT DISTINCT SourceKey as Key FROM Relationships
795
773
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
796
774
  AND TargetKey IN (${matchingKeys.join(',')})
797
775
  ORDER BY SourceKey ASC`;
798
- lsql = `SELECT COUNT(SourceKey) FROM Relationships
776
+ lsql = `SELECT COUNT(DISTINCT SourceKey) FROM Relationships
799
777
  WHERE RelationshipTypeKey = ${this.relationships.get(prop)}
800
778
  AND TargetKey IN (${matchingKeys.join(',')})
801
779
  AND SourceKey = `;
@@ -805,20 +783,14 @@ class LoincServices extends CodeSystemProvider {
805
783
  else if (this.propertyList.has(prop) && op === '=') {
806
784
  let actualValue = value;
807
785
  if (prop === 'CLASSTYPE' && ['1', '2', '3', '4'].includes(value)) {
808
- const classTypes = {
809
- '1': 'Laboratory class',
810
- '2': 'Clinical class',
811
- '3': 'Claims attachments',
812
- '4': 'Surveys'
813
- };
814
786
  actualValue = classTypes[value];
815
787
  }
816
- sql = `SELECT CodeKey as Key FROM Properties, PropertyValues
788
+ sql = `SELECT DISTINCT CodeKey as Key FROM Properties, PropertyValues
817
789
  WHERE Properties.PropertyTypeKey = ${this.propertyList.get(prop)}
818
790
  AND Properties.PropertyValueKey = PropertyValues.PropertyValueKey
819
791
  AND PropertyValues.Value = '${this.#sqlWrapString(actualValue)}' COLLATE NOCASE
820
792
  ORDER BY CodeKey ASC`;
821
- lsql = `SELECT COUNT(CodeKey) FROM Properties, PropertyValues
793
+ lsql = `SELECT COUNT(DISTINCT CodeKey) FROM Properties, PropertyValues
822
794
  WHERE Properties.PropertyTypeKey = ${this.propertyList.get(prop)}
823
795
  AND Properties.PropertyValueKey = PropertyValues.PropertyValueKey
824
796
  AND PropertyValues.Value = '${this.#sqlWrapString(actualValue)}' COLLATE NOCASE
@@ -827,12 +799,12 @@ class LoincServices extends CodeSystemProvider {
827
799
  // Property 'in' filter
828
800
  else if (this.propertyList.has(prop) && op === 'in') {
829
801
  const codes = this.#commaListOfCodes(value);
830
- sql = `SELECT CodeKey as Key FROM Properties, PropertyValues
802
+ sql = `SELECT DISTINCT CodeKey as Key FROM Properties, PropertyValues
831
803
  WHERE Properties.PropertyTypeKey = ${this.propertyList.get(prop)}
832
804
  AND Properties.PropertyValueKey = PropertyValues.PropertyValueKey
833
805
  AND PropertyValues.Value IN (${codes}) COLLATE NOCASE
834
806
  ORDER BY CodeKey ASC`;
835
- lsql = `SELECT COUNT(CodeKey) FROM Properties, PropertyValues
807
+ lsql = `SELECT COUNT(DISTINCT CodeKey) FROM Properties, PropertyValues
836
808
  WHERE Properties.PropertyTypeKey = ${this.propertyList.get(prop)}
837
809
  AND Properties.PropertyValueKey = PropertyValues.PropertyValueKey
838
810
  AND PropertyValues.Value IN (${codes}) COLLATE NOCASE
@@ -857,11 +829,11 @@ class LoincServices extends CodeSystemProvider {
857
829
  'PropertyValueKey'
858
830
  );
859
831
  if (matchingKeys.length > 0) {
860
- sql = `SELECT CodeKey as Key FROM Properties
832
+ sql = `SELECT DISTINCT CodeKey as Key FROM Properties
861
833
  WHERE PropertyTypeKey = ${this.propertyList.get(prop)}
862
834
  AND PropertyValueKey IN (${matchingKeys.join(',')})
863
835
  ORDER BY CodeKey ASC`;
864
- lsql = `SELECT COUNT(CodeKey) FROM Properties
836
+ lsql = `SELECT COUNT(DISTINCT CodeKey) FROM Properties
865
837
  WHERE PropertyTypeKey = ${this.propertyList.get(prop)}
866
838
  AND PropertyValueKey IN (${matchingKeys.join(',')})
867
839
  AND CodeKey = `;
@@ -1000,7 +972,6 @@ class LoincServices extends CodeSystemProvider {
1000
972
  }
1001
973
 
1002
974
  async filterSize(filterContext, set) {
1003
-
1004
975
  return set.keys.length;
1005
976
  }
1006
977
 
@@ -184,6 +184,10 @@ class MimeTypeServices extends CodeSystemProvider {
184
184
  return null;
185
185
  }
186
186
 
187
+ isNotClosed() {
188
+ return true;
189
+ }
190
+
187
191
  }
188
192
 
189
193
  class MimeTypeServicesFactory extends CodeSystemFactoryProvider {
package/tx/cs/cs-ndc.js CHANGED
@@ -109,6 +109,12 @@ class NdcServices extends CodeSystemProvider {
109
109
  return ctxt ? !ctxt.active : false;
110
110
  }
111
111
 
112
+ async getStatus(code) {
113
+
114
+ const ctxt = await this.#ensureContext(code);
115
+ return ctxt.active ? "active" : "inactive";
116
+ }
117
+
112
118
  async isDeprecated(code) {
113
119
  await this.#ensureContext(code);
114
120
  return false; // NDC doesn't track deprecated status separately