fhirsmith 0.18.1 → 1.0.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/ACCESSIBILITY.md +119 -0
- package/CHANGELOG.md +190 -9
- package/CONFIGURATION.md +305 -0
- package/LICENSE +29 -0
- package/README.md +29 -122
- package/cluster.js +1220 -0
- package/cluster.md +707 -0
- package/config-template.json +77 -8
- package/extension-tracker/extension-tracker-template.html +5 -8
- package/extension-tracker/extension-tracker.js +0 -2
- package/folder/folder.js +7 -6
- package/ips/analysis.js +560 -0
- package/ips/ips-template.html +5 -8
- package/ips/ips.js +331 -33
- package/ips/jurisdictions.js +65 -0
- package/ips/nationality.js +120 -0
- package/ips/pipeline.js +131 -17
- package/ips/presenter.js +1 -1
- package/ips/profile-editor.js +283 -4
- package/ips/readme.md +170 -20
- package/ips/render.js +769 -109
- package/ips/scripts.js +46 -0
- package/ips/store.js +123 -11
- package/ips/structure.js +247 -0
- package/ips/terminology.js +1 -1
- package/library/body-limit.js +22 -0
- package/library/cache-generation.js +15 -0
- package/library/cgroup-memory.js +133 -1
- package/library/cluster-counts.js +121 -0
- package/library/cluster-events.js +153 -0
- package/library/cluster-metrics.js +193 -0
- package/library/cluster-status.js +468 -0
- package/library/config-reader.js +96 -0
- package/library/daily-report.js +100 -2
- package/library/html-server.js +27 -1
- package/library/i18nsupport.js +23 -1
- package/library/keep-alive.js +18 -0
- package/library/languages.js +17 -3
- package/library/logger.js +31 -0
- package/library/memory-guard.js +428 -0
- package/library/native-memory.js +52 -0
- package/library/npm-audit.js +51 -25
- package/library/openapi-doc.js +7 -5
- package/library/package-manager.js +8 -0
- package/library/role.js +197 -0
- package/library/server-credentials.js +169 -0
- package/library/sqlite-utilities.js +59 -0
- package/library/ssrf-guard.js +100 -0
- package/library/static-response-stats.js +16 -0
- package/library/version-utilities.js +50 -0
- package/library/worker-count.js +192 -0
- package/nginx.md +30 -11
- package/npmprojector/indexer.js +18 -10
- package/package.json +11 -8
- package/packages/feed-check-page.js +117 -0
- package/packages/feed-check.js +279 -0
- package/packages/npm-check.js +57 -3
- package/packages/npm-signing.js +219 -0
- package/packages/openapi.yaml +207 -1
- package/packages/package-crawler.js +322 -41
- package/packages/packages-template.html +6 -8
- package/packages/packages.js +604 -57
- package/packages/readme.md +113 -1
- package/publisher/publisher-template.html +2 -5
- package/publisher/publisher.js +121 -110
- package/publisher/readme.md +75 -0
- package/registry/api.js +179 -1
- package/registry/crawler.js +86 -108
- package/registry/documentation/api.md +68 -2
- package/registry/documentation/configuration.md +34 -0
- package/registry/documentation/internals.md +4 -1
- package/registry/documentation/readme.md +3 -2
- package/registry/model.js +8 -0
- package/registry/openapi.yaml +84 -0
- package/registry/registry-template.html +5 -8
- package/registry/registry.js +58 -6
- package/root-bare-template.html +7 -4
- package/root-template.html +5 -8
- package/server.js +398 -198
- package/shl/shl.js +38 -37
- package/static/a11y/a11y-check.js +832 -0
- package/static/a11y/axe.min.js +12 -0
- package/static/assets/css/bootstrap-fhir.css +15 -15
- package/static/assets/images/flags/au.svg +18 -0
- package/static/assets/images/flags/unknown.svg +11 -0
- package/static/assets/images/flags/us.svg +63 -0
- package/static/assets/images/icon_audit.png +0 -0
- package/static/assets/images/icon_clinical.png +0 -0
- package/static/assets/images/icon_document.png +0 -0
- package/static/assets/images/icon_external.png +0 -0
- package/static/assets/images/icon_medication.png +0 -0
- package/static/assets/images/icon_organization.png +0 -0
- package/static/assets/images/icon_other.png +0 -0
- package/static/assets/images/icon_person.png +0 -0
- package/static/assets/images/icon_section.png +0 -0
- package/stats-db.js +45 -0
- package/stats.js +197 -10
- package/testing/testing-template.html +5 -8
- package/token/nginx_passport_setup.md +1 -1
- package/token/security_guide.md +1 -1
- package/token/token-template.html +2 -5
- package/token/token.js +40 -36
- package/translations/Messages.properties +19 -0
- package/tx/README.md +158 -4
- package/tx/closure/closure-store.js +126 -3
- package/tx/cm/cm-database.js +69 -57
- package/tx/cm/cm-package.js +50 -16
- package/tx/cs/cs-api.js +284 -19
- package/tx/cs/cs-api.md +41 -4
- package/tx/cs/cs-areacode.js +1 -1
- package/tx/cs/cs-country.js +17 -5
- package/tx/cs/cs-cpt.js +33 -16
- package/tx/cs/cs-cs.js +199 -24
- package/tx/cs/cs-currency.js +1 -1
- package/tx/cs/cs-icd11.js +21 -13
- package/tx/cs/cs-icf.js +22 -3
- package/tx/cs/cs-lang.js +2 -2
- package/tx/cs/cs-loinc.js +193 -67
- package/tx/cs/cs-mimetypes.js +12 -2
- package/tx/cs/cs-ndc.js +40 -25
- package/tx/cs/cs-omop.js +54 -29
- package/tx/cs/cs-rxnorm.js +300 -196
- package/tx/cs/cs-snomed.js +861 -95
- package/tx/cs/cs-ucum.js +14 -4
- package/tx/cs/cs-unii.js +18 -14
- package/tx/cs/cs-virtual.js +210 -0
- package/tx/ecosystem-resolver.js +413 -0
- package/tx/html/cluster-dash-metrics.liquid +26 -0
- package/tx/html/cluster-home-metrics.liquid +31 -0
- package/tx/html/cluster-metrics-script.liquid +251 -0
- package/tx/html/codesystem-operations.liquid +3 -3
- package/tx/html/ecl-panel.liquid +1 -1
- package/tx/html/external-metrics.liquid +17 -0
- package/tx/html/operations-form.liquid +17 -17
- package/tx/html/search-form.liquid +11 -11
- package/tx/html/status-line.liquid +58 -0
- package/tx/html/status-panel.liquid +140 -0
- package/tx/html/status-tasks.liquid +25 -0
- package/tx/html/tx-template.html +2 -5
- package/tx/importers/atc-to-fhir.js +17 -16
- package/tx/importers/export-icd11-codesystem.js +3 -2
- package/tx/importers/import-icd11.js +4 -3
- package/tx/importers/import-loinc.module.js +118 -56
- package/tx/importers/import-ndc.module.js +56 -44
- package/tx/importers/import-rxnorm.module.js +118 -79
- package/tx/importers/import-sct.module.js +125 -61
- package/tx/importers/import-unii.module.js +37 -36
- package/tx/importers/subset-loinc.module.js +49 -48
- package/tx/importers/subset-rxnorm.module.js +29 -28
- package/tx/importers/terminal.js +18 -0
- package/tx/importers/tx-import-base.js +13 -12
- package/tx/importers/tx-import-settings.js +5 -4
- package/tx/importers/tx-import.js +53 -52
- package/tx/library/canonical-resource.js +49 -0
- package/tx/library/codesystem.js +94 -5
- package/tx/library/designations.js +45 -2
- package/tx/library/extensions.js +13 -7
- package/tx/library/implicit-valuesets.js +66 -0
- package/tx/library/renderer.js +77 -21
- package/tx/library/sqlite-utilities.js +2 -0
- package/tx/library/tho-precedence.js +40 -0
- package/tx/library/ucum-parsers.js +11 -1
- package/tx/library/wait-for-cache.js +43 -0
- package/tx/library.js +321 -22
- package/tx/openapi-operations.js +6 -4
- package/tx/operation-context.js +460 -99
- package/tx/params.js +25 -0
- package/tx/problems.js +3 -5
- package/tx/provider.js +314 -67
- package/tx/resource-cache-sqlite.js +1050 -0
- package/tx/sct/documentation.md +45 -21
- package/tx/sct/ecl.js +81 -17
- package/tx/sct/editions.js +39 -0
- package/tx/sct/expressions.js +10 -1
- package/tx/sct/load-config.js +107 -0
- package/tx/sct/mmap.js +109 -0
- package/tx/sct/structures.js +175 -15
- package/tx/sct/text-index.js +5 -1
- package/tx/syndication/syndication-feed.js +2 -2
- package/tx/tx-html.js +44 -8
- package/tx/tx.fhir.org.yml +8 -8
- package/tx/tx.js +282 -80
- package/tx/usage-tracker.js +39 -11
- package/tx/vs/vs-api.js +19 -0
- package/tx/vs/vs-database.js +386 -62
- package/tx/vs/vs-package.js +44 -17
- package/tx/vs/vs-vsac.js +649 -101
- package/tx/workers/batch-validate.js +37 -10
- package/tx/workers/batch.js +6 -1
- package/tx/workers/cache-control.js +15 -4
- package/tx/workers/closure.js +156 -13
- package/tx/workers/compare.js +8 -5
- package/tx/workers/expand.js +1173 -289
- package/tx/workers/metadata.js +35 -0
- package/tx/workers/search.js +53 -1
- package/tx/workers/supplement-filters.js +145 -0
- package/tx/workers/translate.js +169 -4
- package/tx/workers/validate.js +223 -119
- package/tx/workers/worker.js +123 -31
- package/tx/xversion/xv-valueset.js +7 -4
- package/utilities/submit-test-report.js +3 -1
- package/utilities/tx-stress-test.js +482 -0
- package/vcl/readme.md +37 -0
- package/vcl/vcl-parser.js +15 -0
- package/xig/readme.md +52 -0
- package/xig/xig-template.html +5 -8
- package/xig/xig.js +238 -422
- package/changelog-template.md +0 -22
- package/registry/test-runner.js +0 -237
- package/release.sh +0 -80
- package/test-scripts/repro-re2-wasm-leak.js +0 -34
- package/tsconfig.eslint.json +0 -9
- package/tx/html/dash-metrics.liquid +0 -147
- package/tx/html/home-metrics.liquid +0 -225
package/tx/cs/cs-mimetypes.js
CHANGED
|
@@ -741,12 +741,16 @@ class MimeTypeServicesFactory extends CodeSystemFactoryProvider {
|
|
|
741
741
|
* @param {I18nSupport} i18n
|
|
742
742
|
* @param {string} cacheFolder - where the downloaded copy of the IANA registry is kept
|
|
743
743
|
* @param {Logger} log
|
|
744
|
+
* @param {Object} [options]
|
|
745
|
+
* @param {boolean} [options.readOnly] - only read the cached copy: never download or write
|
|
746
|
+
* it (a tx-worker, cluster.md: the master refreshes it before the workers start)
|
|
744
747
|
*/
|
|
745
|
-
constructor(i18n, cacheFolder = null, log = null) {
|
|
748
|
+
constructor(i18n, cacheFolder = null, log = null, options = {}) {
|
|
746
749
|
super(i18n);
|
|
747
750
|
this.uses = 0;
|
|
748
751
|
this.cacheFolder = cacheFolder;
|
|
749
752
|
this.log = log;
|
|
753
|
+
this.readOnly = options.readOnly === true;
|
|
750
754
|
this.registeredTypes = null;
|
|
751
755
|
}
|
|
752
756
|
|
|
@@ -761,7 +765,13 @@ class MimeTypeServicesFactory extends CodeSystemFactoryProvider {
|
|
|
761
765
|
return; // no cache configured: the registered filter is unavailable
|
|
762
766
|
}
|
|
763
767
|
const target = path.join(this.cacheFolder, IANA_MEDIA_TYPES_FILE);
|
|
764
|
-
|
|
768
|
+
if (this.readOnly) {
|
|
769
|
+
if (!fs.existsSync(target)) {
|
|
770
|
+
this.log?.warn(`Mime types: no cached copy of the IANA registry at ${target} (a tx-worker reads the one the master downloads, and never downloads it)`);
|
|
771
|
+
}
|
|
772
|
+
} else {
|
|
773
|
+
await this.#refresh(target);
|
|
774
|
+
}
|
|
765
775
|
|
|
766
776
|
if (fs.existsSync(target)) {
|
|
767
777
|
try {
|
package/tx/cs/cs-ndc.js
CHANGED
|
@@ -3,6 +3,7 @@ const assert = require('assert');
|
|
|
3
3
|
const { CodeSystem } = require('../library/codesystem');
|
|
4
4
|
const { CodeSystemProvider, CodeSystemFactoryProvider} = require('./cs-api');
|
|
5
5
|
const {validateArrayParameter} = require("../../library/utilities");
|
|
6
|
+
const { guarded } = require('../library/sqlite-utilities');
|
|
6
7
|
|
|
7
8
|
class NdcConcept {
|
|
8
9
|
constructor(code, display, isPackage = false, key = null) {
|
|
@@ -31,10 +32,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
31
32
|
|
|
32
33
|
// Clean up database connection when provider is destroyed
|
|
33
34
|
close() {
|
|
34
|
-
|
|
35
|
-
this.db.close();
|
|
36
|
-
this.db = null;
|
|
37
|
-
}
|
|
35
|
+
this.closeDb();
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
// Metadata methods
|
|
@@ -134,6 +132,14 @@ class NdcServices extends CodeSystemProvider {
|
|
|
134
132
|
}
|
|
135
133
|
}
|
|
136
134
|
|
|
135
|
+
/**
|
|
136
|
+
* The concept's properties for $expand: built by extendLookup, so that an expansion reports
|
|
137
|
+
* the same properties $lookup does (see CodeSystemProvider._propertiesFromLookup)
|
|
138
|
+
*/
|
|
139
|
+
async properties(context, names) {
|
|
140
|
+
return this._propertiesFromLookup(context, names);
|
|
141
|
+
}
|
|
142
|
+
|
|
137
143
|
async extendLookup(ctxt, props, params) {
|
|
138
144
|
validateArrayParameter(props, 'props', String);
|
|
139
145
|
validateArrayParameter(params, 'params', Object);
|
|
@@ -248,7 +254,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
248
254
|
params = [concept.key];
|
|
249
255
|
}
|
|
250
256
|
|
|
251
|
-
this.db.get(sql, params, (err, row) => {
|
|
257
|
+
this.db.get(sql, params, guarded(reject, (err, row) => {
|
|
252
258
|
if (err) {
|
|
253
259
|
reject(err);
|
|
254
260
|
} else if (!row) {
|
|
@@ -277,7 +283,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
277
283
|
|
|
278
284
|
resolve(result);
|
|
279
285
|
}
|
|
280
|
-
});
|
|
286
|
+
}));
|
|
281
287
|
});
|
|
282
288
|
}
|
|
283
289
|
|
|
@@ -358,7 +364,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
358
364
|
LIMIT 1
|
|
359
365
|
`;
|
|
360
366
|
|
|
361
|
-
this.db.get(sql, [code, code], (err, row) => {
|
|
367
|
+
this.db.get(sql, [code, code], guarded(reject, (err, row) => {
|
|
362
368
|
if (err) {
|
|
363
369
|
reject(err);
|
|
364
370
|
} else if (row) {
|
|
@@ -370,7 +376,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
370
376
|
} else {
|
|
371
377
|
resolve(null);
|
|
372
378
|
}
|
|
373
|
-
});
|
|
379
|
+
}));
|
|
374
380
|
});
|
|
375
381
|
}
|
|
376
382
|
|
|
@@ -383,7 +389,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
383
389
|
LIMIT 1
|
|
384
390
|
`;
|
|
385
391
|
|
|
386
|
-
this.db.get(sql, [code], (err, row) => {
|
|
392
|
+
this.db.get(sql, [code], guarded(reject, (err, row) => {
|
|
387
393
|
if (err) {
|
|
388
394
|
reject(err);
|
|
389
395
|
} else if (row) {
|
|
@@ -393,7 +399,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
393
399
|
} else {
|
|
394
400
|
resolve(null);
|
|
395
401
|
}
|
|
396
|
-
});
|
|
402
|
+
}));
|
|
397
403
|
});
|
|
398
404
|
}
|
|
399
405
|
|
|
@@ -443,10 +449,10 @@ class NdcServices extends CodeSystemProvider {
|
|
|
443
449
|
return;
|
|
444
450
|
}
|
|
445
451
|
|
|
446
|
-
this.db.get(sql, (err, row) => {
|
|
452
|
+
this.db.get(sql, guarded(reject, (err, row) => {
|
|
447
453
|
if (err) reject(err);
|
|
448
454
|
else resolve(row.count);
|
|
449
|
-
});
|
|
455
|
+
}));
|
|
450
456
|
});
|
|
451
457
|
}
|
|
452
458
|
|
|
@@ -495,7 +501,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
495
501
|
return;
|
|
496
502
|
}
|
|
497
503
|
|
|
498
|
-
this.db.get(sql, [set._iterator.offset], (err, row) => {
|
|
504
|
+
this.db.get(sql, [set._iterator.offset], guarded(reject, (err, row) => {
|
|
499
505
|
if (err) {
|
|
500
506
|
reject(err);
|
|
501
507
|
} else if (row) {
|
|
@@ -514,7 +520,7 @@ class NdcServices extends CodeSystemProvider {
|
|
|
514
520
|
set._iterator.hasMore = false;
|
|
515
521
|
resolve(null);
|
|
516
522
|
}
|
|
517
|
-
});
|
|
523
|
+
}));
|
|
518
524
|
});
|
|
519
525
|
}
|
|
520
526
|
|
|
@@ -571,9 +577,14 @@ class NdcServices extends CodeSystemProvider {
|
|
|
571
577
|
}
|
|
572
578
|
|
|
573
579
|
class NdcServicesFactory extends CodeSystemFactoryProvider {
|
|
574
|
-
|
|
580
|
+
/**
|
|
581
|
+
* @param {Object} [options] - {readOnly: true} in a tx-worker (cluster.md): the file is
|
|
582
|
+
* the master's; this process opens it read-only
|
|
583
|
+
*/
|
|
584
|
+
constructor(i18n, dbPath, options = {}) {
|
|
575
585
|
super(i18n);
|
|
576
586
|
this.dbPath = dbPath;
|
|
587
|
+
this.readOnly = !!options.readOnly;
|
|
577
588
|
this.uses = 0;
|
|
578
589
|
this._loaded = false;
|
|
579
590
|
this._lookupTables = null;
|
|
@@ -601,17 +612,21 @@ class NdcServicesFactory extends CodeSystemFactoryProvider {
|
|
|
601
612
|
}
|
|
602
613
|
}
|
|
603
614
|
|
|
615
|
+
#open() {
|
|
616
|
+
return this.readOnly ? new sqlite3.Database(this.dbPath, sqlite3.OPEN_READONLY) : new sqlite3.Database(this.dbPath);
|
|
617
|
+
}
|
|
618
|
+
|
|
604
619
|
async load() {
|
|
605
620
|
// Use temporary database connection for loading
|
|
606
|
-
const tempDb =
|
|
621
|
+
const tempDb = this.#open();
|
|
607
622
|
|
|
608
623
|
try {
|
|
609
624
|
// Load version
|
|
610
625
|
this._version = await new Promise((resolve, reject) => {
|
|
611
|
-
tempDb.get('SELECT Version FROM NDCVersion ORDER BY Version DESC LIMIT 1', (err, row) => {
|
|
626
|
+
tempDb.get('SELECT Version FROM NDCVersion ORDER BY Version DESC LIMIT 1', guarded(reject, (err, row) => {
|
|
612
627
|
if (err) reject(err);
|
|
613
628
|
else resolve(row ? row.Version : 'unknown');
|
|
614
|
-
});
|
|
629
|
+
}));
|
|
615
630
|
});
|
|
616
631
|
|
|
617
632
|
// Initialize lookup tables
|
|
@@ -632,30 +647,30 @@ class NdcServicesFactory extends CodeSystemFactoryProvider {
|
|
|
632
647
|
|
|
633
648
|
for (const table of tables) {
|
|
634
649
|
await new Promise((resolve, reject) => {
|
|
635
|
-
tempDb.all(table.sql, (err, rows) => {
|
|
650
|
+
tempDb.all(table.sql, guarded(reject, (err, rows) => {
|
|
636
651
|
if (err) reject(err);
|
|
637
652
|
else {
|
|
638
653
|
const map = this._lookupTables[table.name];
|
|
639
654
|
rows.forEach(row => map.set(row.NDCKey, row.Name));
|
|
640
655
|
resolve();
|
|
641
656
|
}
|
|
642
|
-
});
|
|
657
|
+
}));
|
|
643
658
|
});
|
|
644
659
|
}
|
|
645
660
|
|
|
646
661
|
// Load counts
|
|
647
662
|
this._packageCount = await new Promise((resolve, reject) => {
|
|
648
|
-
tempDb.get('SELECT COUNT(NDCKey) as count FROM NDCPackages', (err, row) => {
|
|
663
|
+
tempDb.get('SELECT COUNT(NDCKey) as count FROM NDCPackages', guarded(reject, (err, row) => {
|
|
649
664
|
if (err) reject(err);
|
|
650
665
|
else resolve(row.count);
|
|
651
|
-
});
|
|
666
|
+
}));
|
|
652
667
|
});
|
|
653
668
|
|
|
654
669
|
this._productCount = await new Promise((resolve, reject) => {
|
|
655
|
-
tempDb.get('SELECT COUNT(NDCKey) as count FROM NDCProducts', (err, row) => {
|
|
670
|
+
tempDb.get('SELECT COUNT(NDCKey) as count FROM NDCProducts', guarded(reject, (err, row) => {
|
|
656
671
|
if (err) reject(err);
|
|
657
672
|
else resolve(row.count);
|
|
658
|
-
});
|
|
673
|
+
}));
|
|
659
674
|
});
|
|
660
675
|
|
|
661
676
|
} finally {
|
|
@@ -673,7 +688,7 @@ class NdcServicesFactory extends CodeSystemFactoryProvider {
|
|
|
673
688
|
this.recordUse();
|
|
674
689
|
|
|
675
690
|
// Create fresh database connection for this provider instance
|
|
676
|
-
const db =
|
|
691
|
+
const db = this.#open();
|
|
677
692
|
|
|
678
693
|
return new NdcServices(
|
|
679
694
|
opContext, supplements,
|
package/tx/cs/cs-omop.js
CHANGED
|
@@ -5,6 +5,7 @@ const { FilterExecutionContext, CodeSystemFactoryProvider } = require('./cs-api'
|
|
|
5
5
|
const {validateOptionalParameter, validateArrayParameter} = require("../../library/utilities");
|
|
6
6
|
const {ConceptMap} = require("../library/conceptmap");
|
|
7
7
|
const {BaseCSServices} = require("./cs-base");
|
|
8
|
+
const { guarded } = require('../library/sqlite-utilities');
|
|
8
9
|
|
|
9
10
|
class OMOPConcept {
|
|
10
11
|
constructor(code, display, domain, conceptClass, standard, vocabulary) {
|
|
@@ -135,10 +136,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
close() {
|
|
138
|
-
|
|
139
|
-
this.db.close();
|
|
140
|
-
this.db = null;
|
|
141
|
-
}
|
|
139
|
+
this.closeDb();
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
// Metadata methods
|
|
@@ -166,10 +164,10 @@ class OMOPServices extends BaseCSServices {
|
|
|
166
164
|
|
|
167
165
|
async totalCount() {
|
|
168
166
|
return new Promise((resolve, reject) => {
|
|
169
|
-
this.db.get('SELECT COUNT(*) as count FROM Concepts', (err, row) => {
|
|
167
|
+
this.db.get('SELECT COUNT(*) as count FROM Concepts', guarded(reject, (err, row) => {
|
|
170
168
|
if (err) reject(err);
|
|
171
169
|
else resolve(row.count);
|
|
172
|
-
});
|
|
170
|
+
}));
|
|
173
171
|
});
|
|
174
172
|
}
|
|
175
173
|
|
|
@@ -245,7 +243,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
245
243
|
AND ConceptSynonyms.concept_id = ?
|
|
246
244
|
`;
|
|
247
245
|
|
|
248
|
-
this.db.all(sql, [code], (err, rows) => {
|
|
246
|
+
this.db.all(sql, [code], guarded(reject, (err, rows) => {
|
|
249
247
|
if (err) {
|
|
250
248
|
reject(err);
|
|
251
249
|
} else {
|
|
@@ -255,10 +253,26 @@ class OMOPServices extends BaseCSServices {
|
|
|
255
253
|
}));
|
|
256
254
|
resolve(synonyms);
|
|
257
255
|
}
|
|
258
|
-
});
|
|
256
|
+
}));
|
|
259
257
|
});
|
|
260
258
|
}
|
|
261
259
|
|
|
260
|
+
/**
|
|
261
|
+
* The concept's properties for $expand: built by extendLookup, so that an expansion reports
|
|
262
|
+
* the same properties $lookup does (see CodeSystemProvider._propertiesFromLookup)
|
|
263
|
+
*/
|
|
264
|
+
async properties(context, names) {
|
|
265
|
+
// a concept from a filter is partial (domain_id is the domain's concept id, no concept class
|
|
266
|
+
// or vocabulary): the properties are those of the concept itself
|
|
267
|
+
if (names && names.length > 0 && context instanceof OMOPConcept && context.conceptClass === '') {
|
|
268
|
+
const located = await this.locate(String(context.code));
|
|
269
|
+
if (located.context) {
|
|
270
|
+
context = located.context;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return this._propertiesFromLookup(context, names);
|
|
274
|
+
}
|
|
275
|
+
|
|
262
276
|
async extendLookup(ctxt, props, params) {
|
|
263
277
|
validateArrayParameter(props, 'props', String);
|
|
264
278
|
validateArrayParameter(params, 'params', Object);
|
|
@@ -307,7 +321,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
307
321
|
return new Promise((resolve, reject) => {
|
|
308
322
|
const sql = 'SELECT * FROM Concepts WHERE concept_id = ?';
|
|
309
323
|
|
|
310
|
-
this.db.get(sql, [ctxt.code], (err, row) => {
|
|
324
|
+
this.db.get(sql, [ctxt.code], guarded(reject, (err, row) => {
|
|
311
325
|
if (err) {
|
|
312
326
|
reject(err);
|
|
313
327
|
} else if (row) {
|
|
@@ -337,7 +351,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
337
351
|
} else {
|
|
338
352
|
resolve();
|
|
339
353
|
}
|
|
340
|
-
});
|
|
354
|
+
}));
|
|
341
355
|
});
|
|
342
356
|
}
|
|
343
357
|
|
|
@@ -354,7 +368,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
354
368
|
AND ConceptRelationships.concept_id_1 = ?
|
|
355
369
|
`;
|
|
356
370
|
|
|
357
|
-
this.db.all(sql, [ctxt.code], (err, rows) => {
|
|
371
|
+
this.db.all(sql, [ctxt.code], guarded(reject, (err, rows) => {
|
|
358
372
|
if (err) {
|
|
359
373
|
reject(err);
|
|
360
374
|
} else {
|
|
@@ -367,7 +381,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
367
381
|
}
|
|
368
382
|
resolve();
|
|
369
383
|
}
|
|
370
|
-
});
|
|
384
|
+
}));
|
|
371
385
|
});
|
|
372
386
|
|
|
373
387
|
// Reverse relationships
|
|
@@ -380,7 +394,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
380
394
|
AND ConceptRelationships.concept_id_2 = ?
|
|
381
395
|
`;
|
|
382
396
|
|
|
383
|
-
this.db.all(sql, [ctxt.code], (err, rows) => {
|
|
397
|
+
this.db.all(sql, [ctxt.code], guarded(reject, (err, rows) => {
|
|
384
398
|
if (err) {
|
|
385
399
|
reject(err);
|
|
386
400
|
} else {
|
|
@@ -394,7 +408,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
394
408
|
}
|
|
395
409
|
resolve();
|
|
396
410
|
}
|
|
397
|
-
});
|
|
411
|
+
}));
|
|
398
412
|
});
|
|
399
413
|
}
|
|
400
414
|
|
|
@@ -512,7 +526,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
512
526
|
AND concept_id = ?
|
|
513
527
|
`;
|
|
514
528
|
|
|
515
|
-
this.db.get(sql, [code], (err, row) => {
|
|
529
|
+
this.db.get(sql, [code], guarded(reject, (err, row) => {
|
|
516
530
|
if (err) {
|
|
517
531
|
reject(err);
|
|
518
532
|
} else if (row && row.concept_id.toString() === code) {
|
|
@@ -528,7 +542,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
528
542
|
} else {
|
|
529
543
|
resolve({ context: null, message: undefined });
|
|
530
544
|
}
|
|
531
|
-
});
|
|
545
|
+
}));
|
|
532
546
|
});
|
|
533
547
|
}
|
|
534
548
|
|
|
@@ -605,7 +619,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
605
619
|
set.cursor++;
|
|
606
620
|
|
|
607
621
|
return new OMOPConcept(
|
|
608
|
-
row.concept_id,
|
|
622
|
+
String(row.concept_id), // a code is a string (the expansion's code was a JSON number)
|
|
609
623
|
row.concept_name,
|
|
610
624
|
row.domain_id,
|
|
611
625
|
'', // concept_class not in basic filter query
|
|
@@ -657,7 +671,9 @@ class OMOPServices extends BaseCSServices {
|
|
|
657
671
|
}
|
|
658
672
|
|
|
659
673
|
|
|
660
|
-
// Subsumption
|
|
674
|
+
// Subsumption: this provider has no hierarchy (the OMOP concept_ancestor table isn't
|
|
675
|
+
// imported), so two concepts that both exist are not-subsumed (the caller deals with equal
|
|
676
|
+
// codes: equivalent)
|
|
661
677
|
async subsumesTest(codeA, codeB) {
|
|
662
678
|
await this.#ensureContext(codeA);
|
|
663
679
|
await this.#ensureContext(codeB);
|
|
@@ -683,7 +699,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
683
699
|
WHERE concept_id = ? AND vocabulary_id = ?
|
|
684
700
|
`;
|
|
685
701
|
|
|
686
|
-
this.db.all(sql, [coding.code, vocabId], (err, rows) => {
|
|
702
|
+
this.db.all(sql, [coding.code, vocabId], guarded(reject, (err, rows) => {
|
|
687
703
|
if (err) {
|
|
688
704
|
reject(err);
|
|
689
705
|
} else {
|
|
@@ -696,7 +712,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
696
712
|
}));
|
|
697
713
|
resolve(translations);
|
|
698
714
|
}
|
|
699
|
-
});
|
|
715
|
+
}));
|
|
700
716
|
});
|
|
701
717
|
}
|
|
702
718
|
|
|
@@ -712,7 +728,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
712
728
|
AND Domains.domain_concept_id = Concepts.concept_id
|
|
713
729
|
`;
|
|
714
730
|
|
|
715
|
-
this.db.get(sql, [domain], (err, row) => {
|
|
731
|
+
this.db.get(sql, [domain], guarded(reject, (err, row) => {
|
|
716
732
|
if (err) {
|
|
717
733
|
reject(err);
|
|
718
734
|
} else if (row && row.domain_id === domain) {
|
|
@@ -740,7 +756,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
740
756
|
} else {
|
|
741
757
|
reject(new Error(`Unknown Value Domain ${id}`));
|
|
742
758
|
}
|
|
743
|
-
});
|
|
759
|
+
}));
|
|
744
760
|
});
|
|
745
761
|
}
|
|
746
762
|
|
|
@@ -749,7 +765,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
749
765
|
return new Promise((resolve, reject) => {
|
|
750
766
|
const sql = 'SELECT DISTINCT vocabulary_id FROM Concepts';
|
|
751
767
|
|
|
752
|
-
this.db.all(sql, (err, rows) => {
|
|
768
|
+
this.db.all(sql, guarded(reject, (err, rows) => {
|
|
753
769
|
if (err) {
|
|
754
770
|
reject(err);
|
|
755
771
|
} else {
|
|
@@ -774,7 +790,7 @@ class OMOPServices extends BaseCSServices {
|
|
|
774
790
|
}
|
|
775
791
|
resolve();
|
|
776
792
|
}
|
|
777
|
-
});
|
|
793
|
+
}));
|
|
778
794
|
});
|
|
779
795
|
}
|
|
780
796
|
|
|
@@ -784,9 +800,14 @@ class OMOPServices extends BaseCSServices {
|
|
|
784
800
|
}
|
|
785
801
|
|
|
786
802
|
class OMOPServicesFactory extends CodeSystemFactoryProvider {
|
|
787
|
-
|
|
803
|
+
/**
|
|
804
|
+
* @param {Object} [options] - {readOnly: true} in a tx-worker (cluster.md): the file is
|
|
805
|
+
* the master's; this process opens it read-only
|
|
806
|
+
*/
|
|
807
|
+
constructor(i18n, dbPath, options = {}) {
|
|
788
808
|
super(i18n);
|
|
789
809
|
this.dbPath = dbPath;
|
|
810
|
+
this.readOnly = !!options.readOnly;
|
|
790
811
|
this.uses = 0;
|
|
791
812
|
this._loaded = false;
|
|
792
813
|
this._sharedData = null;
|
|
@@ -855,8 +876,12 @@ class OMOPServicesFactory extends CodeSystemFactoryProvider {
|
|
|
855
876
|
}
|
|
856
877
|
}
|
|
857
878
|
|
|
879
|
+
#open() {
|
|
880
|
+
return this.readOnly ? new sqlite3.Database(this.dbPath, sqlite3.OPEN_READONLY) : new sqlite3.Database(this.dbPath);
|
|
881
|
+
}
|
|
882
|
+
|
|
858
883
|
async load() {
|
|
859
|
-
const db =
|
|
884
|
+
const db = this.#open();
|
|
860
885
|
|
|
861
886
|
try {
|
|
862
887
|
this._sharedData = {
|
|
@@ -880,7 +905,7 @@ class OMOPServicesFactory extends CodeSystemFactoryProvider {
|
|
|
880
905
|
WHERE vocabulary_id = 'OMOP Extension'
|
|
881
906
|
`;
|
|
882
907
|
|
|
883
|
-
db.get(sql, (err, row) => {
|
|
908
|
+
db.get(sql, guarded(reject, (err, row) => {
|
|
884
909
|
if (err) {
|
|
885
910
|
reject(err);
|
|
886
911
|
} else if (row) {
|
|
@@ -894,7 +919,7 @@ class OMOPServicesFactory extends CodeSystemFactoryProvider {
|
|
|
894
919
|
this._sharedData._version = 'unknown';
|
|
895
920
|
resolve();
|
|
896
921
|
}
|
|
897
|
-
});
|
|
922
|
+
}));
|
|
898
923
|
});
|
|
899
924
|
}
|
|
900
925
|
|
|
@@ -907,7 +932,7 @@ class OMOPServicesFactory extends CodeSystemFactoryProvider {
|
|
|
907
932
|
this.recordUse();
|
|
908
933
|
|
|
909
934
|
// Create fresh database connection for this provider instance
|
|
910
|
-
const db =
|
|
935
|
+
const db = this.#open();
|
|
911
936
|
|
|
912
937
|
return new OMOPServices(opContext, supplements, db, this._sharedData);
|
|
913
938
|
}
|