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
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
The background tasks under the graphs (stats.taskSummary): one line naming those at work, those
|
|
3
|
+
whose last run failed (each is a problem in the status line, with its state) and the idle ones,
|
|
4
|
+
then a row for each at work. Styles are in status-line.liquid; every task is under "More".
|
|
5
|
+
{%- endcomment -%}
|
|
6
|
+
<section class="fs-status fs-status-jobs" aria-labelledby="fs-status-jobs-title">
|
|
7
|
+
<div class="fs-status-jobs-head">
|
|
8
|
+
<h{{ level }} id="fs-status-jobs-title">Background tasks</h{{ level }}>
|
|
9
|
+
<span class="fs-status-idle">
|
|
10
|
+
{%- if tasks.working.size == 0 %}none at work{% else %}{{ tasks.working.size }} at work{% endif -%}
|
|
11
|
+
{%- if tasks.failed.size > 0 %} · {{ tasks.failed.size }} failed, under Problems: {{ tasks.failed | join: ', ' | escape }}{% endif -%}
|
|
12
|
+
{%- if tasks.idle.size > 0 %} · {{ tasks.idle.size }} idle: {{ tasks.idle | join: ', ' | escape }}{% endif -%}
|
|
13
|
+
</span>
|
|
14
|
+
</div>
|
|
15
|
+
{%- if tasks.working.size > 0 %}
|
|
16
|
+
<table>
|
|
17
|
+
<thead><tr><th>Task</th><th>Status</th><th>Frequency</th><th>Last seen</th></tr></thead>
|
|
18
|
+
<tbody>
|
|
19
|
+
{%- for t in tasks.working %}
|
|
20
|
+
<tr><td>{{ t.name | escape }}</td><td>{{ t.state | escape }}</td><td>{{ t.frequency | escape }}</td><td>{{ t.ago | escape }}</td></tr>
|
|
21
|
+
{%- endfor %}
|
|
22
|
+
</tbody>
|
|
23
|
+
</table>
|
|
24
|
+
{%- endif %}
|
|
25
|
+
</section>
|
package/tx/html/tx-template.html
CHANGED
|
@@ -83,8 +83,6 @@
|
|
|
83
83
|
<div id="stripe"> </div>
|
|
84
84
|
<div class="container">
|
|
85
85
|
<div style="background-color: #ad1f2f; padding: 6px; color: white;">
|
|
86
|
-
<a href="http://www.hl7.org/fhir" style="color: gold" title="Fast Healthcare Interoperability Resources - Home Page"><img border="0" src="/icon-fhir-16.png" style="vertical-align: text-bottom"/> <b>FHIR</b></a> [%fhir-version%] © HL7.org |
|
|
87
|
-
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] |
|
|
88
86
|
<a href="/" style="color: gold"> Server Home</a> |
|
|
89
87
|
<a href="[%endpoint-path%]" style="color: gold">TX Home</a> |
|
|
90
88
|
<a href="[%endpoint-path%]/metadata" style="color: gold">Capability Statement</a> |
|
|
@@ -105,7 +103,7 @@
|
|
|
105
103
|
<div class="inner-wrapper">
|
|
106
104
|
<div class="col-12">
|
|
107
105
|
|
|
108
|
-
|
|
106
|
+
<h2><a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" title="FHIRsmith [%ver%]"><img border="0" src="/FHIRsmith32.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/></a> TX: [%title%] </h2>
|
|
109
107
|
|
|
110
108
|
[%content%]
|
|
111
109
|
|
|
@@ -121,8 +119,7 @@
|
|
|
121
119
|
<div class="container">
|
|
122
120
|
<div class="inner-wrapper">
|
|
123
121
|
<p>
|
|
124
|
-
<a href="
|
|
125
|
-
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] © HealthIntersections.com.au 2023+ |
|
|
122
|
+
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] © HealthIntersections.com.au 2023+ |
|
|
126
123
|
([%ms%] ms)
|
|
127
124
|
[%sponsorMessage%]
|
|
128
125
|
</p>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const { XMLParser } = require('fast-xml-parser');
|
|
3
|
+
const terminal = require('./terminal');
|
|
3
4
|
|
|
4
5
|
// Configuration
|
|
5
6
|
const ATC_FILE = process.argv[2] || '2025_ATC.xml';
|
|
@@ -147,21 +148,21 @@ function buildConcept(atcRow, dddMap) {
|
|
|
147
148
|
|
|
148
149
|
// Main conversion function
|
|
149
150
|
function convertATCtoFHIR(atcFile, dddFile) {
|
|
150
|
-
|
|
151
|
+
terminal.log(`Parsing ${atcFile}...`);
|
|
151
152
|
const atcParsed = parseXML(atcFile);
|
|
152
153
|
const atcRows = extractRows(atcParsed);
|
|
153
|
-
|
|
154
|
+
terminal.log(` Found ${atcRows.length} ATC codes`);
|
|
154
155
|
|
|
155
|
-
|
|
156
|
+
terminal.log(`Parsing ${dddFile}...`);
|
|
156
157
|
const dddParsed = parseXML(dddFile);
|
|
157
158
|
const dddRows = extractRows(dddParsed);
|
|
158
|
-
|
|
159
|
+
terminal.log(` Found ${dddRows.length} DDD entries`);
|
|
159
160
|
|
|
160
|
-
|
|
161
|
+
terminal.log('Building DDD lookup map...');
|
|
161
162
|
const dddMap = buildDDDMap(dddRows);
|
|
162
|
-
|
|
163
|
+
terminal.log(` ${dddMap.size} codes have DDD data`);
|
|
163
164
|
|
|
164
|
-
|
|
165
|
+
terminal.log('Building FHIR CodeSystem...');
|
|
165
166
|
|
|
166
167
|
const codeSystem = {
|
|
167
168
|
resourceType: 'CodeSystem',
|
|
@@ -256,7 +257,7 @@ function convertATCtoFHIR(atcFile, dddFile) {
|
|
|
256
257
|
parent.concept.push(concept);
|
|
257
258
|
} else {
|
|
258
259
|
// Parent not found, add to root (shouldn't happen with valid data)
|
|
259
|
-
|
|
260
|
+
terminal.warn(`Warning: Parent ${parentCode} not found for ${code}`);
|
|
260
261
|
rootConcepts.push(concept);
|
|
261
262
|
}
|
|
262
263
|
}
|
|
@@ -265,7 +266,7 @@ function convertATCtoFHIR(atcFile, dddFile) {
|
|
|
265
266
|
codeSystem.concept = rootConcepts;
|
|
266
267
|
codeSystem.count = conceptMap.size;
|
|
267
268
|
|
|
268
|
-
|
|
269
|
+
terminal.log(` Generated ${conceptMap.size} concepts in nested hierarchy`);
|
|
269
270
|
|
|
270
271
|
return codeSystem;
|
|
271
272
|
}
|
|
@@ -274,19 +275,19 @@ function convertATCtoFHIR(atcFile, dddFile) {
|
|
|
274
275
|
try {
|
|
275
276
|
const codeSystem = convertATCtoFHIR(ATC_FILE, DDD_FILE);
|
|
276
277
|
|
|
277
|
-
|
|
278
|
+
terminal.log(`Writing to ${OUTPUT_FILE}...`);
|
|
278
279
|
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(codeSystem, null, 2));
|
|
279
|
-
|
|
280
|
+
terminal.log('Done!');
|
|
280
281
|
|
|
281
282
|
const totalConcepts = countConcepts(codeSystem.concept);
|
|
282
283
|
const withDDD = countWithDDD(codeSystem.concept);
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
terminal.log(`\nStatistics:`);
|
|
285
|
+
terminal.log(` Total concepts: ${totalConcepts}`);
|
|
286
|
+
terminal.log(` Top-level concepts: ${codeSystem.concept.length}`);
|
|
287
|
+
terminal.log(` Concepts with DDD data: ${withDDD}`);
|
|
287
288
|
|
|
288
289
|
} catch (error) {
|
|
289
|
-
|
|
290
|
+
terminal.error('Error:', error.message);
|
|
290
291
|
process.exit(1);
|
|
291
292
|
}
|
|
292
293
|
|
|
@@ -24,6 +24,7 @@ const crypto = require('crypto');
|
|
|
24
24
|
const Database = require('better-sqlite3');
|
|
25
25
|
const { Command } = require('commander');
|
|
26
26
|
const { axisLabel } = require('../cs/icd11-expressions');
|
|
27
|
+
const terminal = require('./terminal');
|
|
27
28
|
|
|
28
29
|
const TOOL_VERSION = '1.0.0';
|
|
29
30
|
const SCHEMA_NS = 'http://id.who.int/icd/schema';
|
|
@@ -141,7 +142,7 @@ class CodeSystemExporter {
|
|
|
141
142
|
constructor(opts) {
|
|
142
143
|
this.opts = opts;
|
|
143
144
|
this.db = new Database(opts.src, { readonly: true });
|
|
144
|
-
this.log = (...a) =>
|
|
145
|
+
this.log = (...a) => terminal.log(...a);
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
meta(key) {
|
|
@@ -861,7 +862,7 @@ function main(argv) {
|
|
|
861
862
|
|
|
862
863
|
if (require.main === module) {
|
|
863
864
|
main(process.argv).catch(e => {
|
|
864
|
-
|
|
865
|
+
terminal.error(`\nexport failed: ${e && e.stack || e}`);
|
|
865
866
|
process.exit(1);
|
|
866
867
|
});
|
|
867
868
|
}
|
|
@@ -26,6 +26,7 @@ const fs = require('fs');
|
|
|
26
26
|
const path = require('path');
|
|
27
27
|
const Database = require('better-sqlite3');
|
|
28
28
|
const { Command } = require('commander');
|
|
29
|
+
const terminal = require('./terminal');
|
|
29
30
|
|
|
30
31
|
const TOOL_VERSION = '1.0.0';
|
|
31
32
|
const WHO = 'http://id.who.int';
|
|
@@ -341,8 +342,8 @@ class Icd11Importer {
|
|
|
341
342
|
this.native = stripTrailingSlash(opts.native);
|
|
342
343
|
this.fhir = opts.fhir ? stripTrailingSlash(opts.fhir) : null;
|
|
343
344
|
this.fetcher = new Fetcher(opts);
|
|
344
|
-
this.log = (...a) =>
|
|
345
|
-
this.warn = (...a) =>
|
|
345
|
+
this.log = (...a) => terminal.log(...a);
|
|
346
|
+
this.warn = (...a) => terminal.warn(...a);
|
|
346
347
|
}
|
|
347
348
|
|
|
348
349
|
headers(lang) {
|
|
@@ -892,7 +893,7 @@ function main(argv) {
|
|
|
892
893
|
|
|
893
894
|
if (require.main === module) {
|
|
894
895
|
main(process.argv).catch(e => {
|
|
895
|
-
|
|
896
|
+
terminal.error(`\nimport failed: ${e && e.stack || e}`);
|
|
896
897
|
process.exit(1);
|
|
897
898
|
});
|
|
898
899
|
}
|
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const readline = require('readline');
|
|
6
6
|
const { csvRecords } = require('./csv-records');
|
|
7
7
|
const chalk = require('chalk');
|
|
8
|
+
const terminal = require('./terminal');
|
|
8
9
|
|
|
9
10
|
class LoincModule extends BaseTerminologyModule {
|
|
10
11
|
constructor() {
|
|
@@ -109,7 +110,7 @@ class LoincModule extends BaseTerminologyModule {
|
|
|
109
110
|
} catch (error) {
|
|
110
111
|
this.logError(`Import command failed: ${error.message}`);
|
|
111
112
|
if (options.verbose) {
|
|
112
|
-
|
|
113
|
+
terminal.error(error.stack);
|
|
113
114
|
}
|
|
114
115
|
throw error;
|
|
115
116
|
}
|
|
@@ -119,17 +120,17 @@ class LoincModule extends BaseTerminologyModule {
|
|
|
119
120
|
const inquirer = require('inquirer');
|
|
120
121
|
const chalk = require('chalk');
|
|
121
122
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
terminal.log(chalk.cyan(`\n📋 ${this.getName()} Import Configuration:`));
|
|
124
|
+
terminal.log(` Source: ${chalk.white(config.source)}`);
|
|
125
|
+
terminal.log(` Destination: ${chalk.white(config.dest)}`);
|
|
126
|
+
terminal.log(` Version: ${chalk.white(config.version)}`);
|
|
127
|
+
terminal.log(` Main Only: ${chalk.white(config.mainOnly ? 'Yes' : 'No')}`);
|
|
128
|
+
terminal.log(` Create Indexes: ${chalk.white(config.createIndexes ? 'Yes' : 'No')}`);
|
|
129
|
+
terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
|
|
130
|
+
terminal.log(` Verbose: ${chalk.white(config.verbose ? 'Yes' : 'No')}`);
|
|
130
131
|
|
|
131
132
|
if (config.estimatedDuration) {
|
|
132
|
-
|
|
133
|
+
terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
const { confirmed } = await inquirer.prompt({
|
|
@@ -144,7 +145,7 @@ class LoincModule extends BaseTerminologyModule {
|
|
|
144
145
|
|
|
145
146
|
async runImportWithoutConfigSaving(config) {
|
|
146
147
|
try {
|
|
147
|
-
|
|
148
|
+
terminal.log(chalk.blue.bold(`🏥 Starting ${this.getName()} Import...\n`));
|
|
148
149
|
|
|
149
150
|
// Pre-flight checks
|
|
150
151
|
this.logInfo('Running pre-flight checks...');
|
|
@@ -163,7 +164,7 @@ class LoincModule extends BaseTerminologyModule {
|
|
|
163
164
|
this.stopProgress();
|
|
164
165
|
this.logError(`${this.getName()} import failed: ${error.message}`);
|
|
165
166
|
if (config.verbose) {
|
|
166
|
-
|
|
167
|
+
terminal.error(error.stack);
|
|
167
168
|
}
|
|
168
169
|
process.exit(1);
|
|
169
170
|
}
|
|
@@ -186,14 +187,14 @@ class LoincModule extends BaseTerminologyModule {
|
|
|
186
187
|
const stats = await this.validateLoincDirectory(options.source);
|
|
187
188
|
|
|
188
189
|
this.logSuccess('Directory validation passed');
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
190
|
+
terminal.log(` Main codes file: ${stats.mainCodesFound ? 'Found' : 'Missing'}`);
|
|
191
|
+
terminal.log(` Parts file: ${stats.partsFound ? 'Found' : 'Missing'}`);
|
|
192
|
+
terminal.log(` Language variants: ${stats.languageVariants.length} found`);
|
|
193
|
+
terminal.log(` Estimated main codes: ${stats.estimatedCodes.toLocaleString()}`);
|
|
193
194
|
|
|
194
195
|
if (stats.warnings.length > 0) {
|
|
195
196
|
this.logWarning('Validation warnings:');
|
|
196
|
-
stats.warnings.forEach(warning =>
|
|
197
|
+
stats.warnings.forEach(warning => terminal.log(` ${warning}`));
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
} catch (error) {
|
|
@@ -215,15 +216,15 @@ class LoincModule extends BaseTerminologyModule {
|
|
|
215
216
|
const stats = await this.getDatabaseStats(dbPath);
|
|
216
217
|
|
|
217
218
|
this.logSuccess('Database status:');
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
219
|
+
terminal.log(` Version: ${stats.version}`);
|
|
220
|
+
terminal.log(` Total Codes: ${stats.codeCount.toLocaleString()}`);
|
|
221
|
+
terminal.log(` Main LOINC Codes: ${stats.mainCodeCount.toLocaleString()}`);
|
|
222
|
+
terminal.log(` Parts: ${stats.partCount.toLocaleString()}`);
|
|
223
|
+
terminal.log(` Answer Lists: ${stats.answerListCount.toLocaleString()}`);
|
|
224
|
+
terminal.log(` Languages: ${stats.languageCount.toLocaleString()}`);
|
|
225
|
+
terminal.log(` Relationships: ${stats.relationshipCount.toLocaleString()}`);
|
|
226
|
+
terminal.log(` Database Size: ${stats.sizeGB.toFixed(2)} GB`);
|
|
227
|
+
terminal.log(` Last Modified: ${stats.lastModified}`);
|
|
227
228
|
|
|
228
229
|
} catch (error) {
|
|
229
230
|
this.logError(`Status check failed: ${error.message}`);
|
|
@@ -446,7 +447,7 @@ class LoincModule extends BaseTerminologyModule {
|
|
|
446
447
|
db.serialize(() => {
|
|
447
448
|
indexes.forEach(sql => {
|
|
448
449
|
db.run(sql, (err) => {
|
|
449
|
-
if (err)
|
|
450
|
+
if (err) terminal.warn(`Index creation warning: ${err.message}`);
|
|
450
451
|
});
|
|
451
452
|
});
|
|
452
453
|
});
|
|
@@ -579,7 +580,7 @@ class LoincDataMigrator {
|
|
|
579
580
|
}
|
|
580
581
|
|
|
581
582
|
async migrate(sourceDir, destFile, version = 'unknown', options = {}) {
|
|
582
|
-
if (options.verbose)
|
|
583
|
+
if (options.verbose) terminal.log('Starting LOINC data migration...');
|
|
583
584
|
|
|
584
585
|
const destDir = path.dirname(destFile);
|
|
585
586
|
if (!fs.existsSync(destDir)) {
|
|
@@ -591,11 +592,21 @@ class LoincDataMigrator {
|
|
|
591
592
|
}
|
|
592
593
|
|
|
593
594
|
const db = new sqlite3.Database(destFile);
|
|
595
|
+
// Run every statement in submission order, and the whole import as one transaction:
|
|
596
|
+
// without it each INSERT autocommits and waits on its own fsync (~10 ms each), which
|
|
597
|
+
// made even the small test fixture take minutes
|
|
598
|
+
db.serialize();
|
|
599
|
+
const exec = (sql) => new Promise((resolve, reject) => db.exec(sql, (err) => err ? reject(err) : resolve()));
|
|
594
600
|
|
|
595
601
|
try {
|
|
602
|
+
await exec('BEGIN');
|
|
603
|
+
|
|
596
604
|
// Initialize tracking variables
|
|
597
605
|
this.codeKey = 0;
|
|
598
606
|
this.relKey = 0;
|
|
607
|
+
this.relSeen = new Set();
|
|
608
|
+
this.axisParts = new Map(); // term key -> { COMPONENT: part key, ... } (see processLanguage)
|
|
609
|
+
this.partNamesSeen = new Set();
|
|
599
610
|
this.descKey = 0;
|
|
600
611
|
this.propKey = 0;
|
|
601
612
|
this.propValueKey = 0;
|
|
@@ -667,14 +678,15 @@ class LoincDataMigrator {
|
|
|
667
678
|
}
|
|
668
679
|
}
|
|
669
680
|
|
|
670
|
-
|
|
681
|
+
await exec('COMMIT');
|
|
682
|
+
if (options.verbose) terminal.log('LOINC data migration completed successfully');
|
|
671
683
|
} finally {
|
|
672
684
|
await this.closeDatabase(db, options.verbose);
|
|
673
685
|
}
|
|
674
686
|
}
|
|
675
687
|
|
|
676
688
|
async createTables(db, version, verbose = true) {
|
|
677
|
-
if (verbose)
|
|
689
|
+
if (verbose) terminal.log('Creating database tables...');
|
|
678
690
|
|
|
679
691
|
const tableSQL = [
|
|
680
692
|
`CREATE TABLE Config (
|
|
@@ -775,7 +787,7 @@ class LoincDataMigrator {
|
|
|
775
787
|
});
|
|
776
788
|
|
|
777
789
|
this.insertInitialData(db, version);
|
|
778
|
-
if (verbose)
|
|
790
|
+
if (verbose) terminal.log('Database tables created');
|
|
779
791
|
resolve();
|
|
780
792
|
});
|
|
781
793
|
});
|
|
@@ -826,7 +838,8 @@ class LoincDataMigrator {
|
|
|
826
838
|
// Description types
|
|
827
839
|
const descriptionTypes = [
|
|
828
840
|
[1, 'LONG_COMMON_NAME'], [2, 'SHORTNAME'], [3, 'ConsumerName'],
|
|
829
|
-
[4, 'RELATEDNAMES2'], [5, 'DisplayName'], [6, 'LinguisticVariantDisplayName']
|
|
841
|
+
[4, 'RELATEDNAMES2'], [5, 'DisplayName'], [6, 'LinguisticVariantDisplayName'],
|
|
842
|
+
[7, 'PartName']
|
|
830
843
|
];
|
|
831
844
|
descriptionTypes.forEach(([key, desc]) => {
|
|
832
845
|
db.run('INSERT INTO DescriptionTypes (DescriptionTypeKey, Description) VALUES (?, ?)', [key, desc]);
|
|
@@ -869,11 +882,11 @@ class LoincDataMigrator {
|
|
|
869
882
|
}
|
|
870
883
|
|
|
871
884
|
async processLanguageVariants(db, sourceDir, step, options) {
|
|
872
|
-
if (options.verbose)
|
|
885
|
+
if (options.verbose) terminal.log('Processing Language Variants...');
|
|
873
886
|
|
|
874
887
|
const filePath = path.join(sourceDir, 'AccessoryFiles/LinguisticVariants/LinguisticVariants.csv');
|
|
875
888
|
if (!fs.existsSync(filePath)) {
|
|
876
|
-
if (options.verbose)
|
|
889
|
+
if (options.verbose) terminal.warn(`Language variants file not found: ${filePath}`);
|
|
877
890
|
return;
|
|
878
891
|
}
|
|
879
892
|
|
|
@@ -900,7 +913,7 @@ class LoincDataMigrator {
|
|
|
900
913
|
}
|
|
901
914
|
|
|
902
915
|
async processParts(db, sourceDir, step, options) {
|
|
903
|
-
if (options.verbose)
|
|
916
|
+
if (options.verbose) terminal.log('Processing Parts...');
|
|
904
917
|
|
|
905
918
|
const filePath = path.join(sourceDir, 'AccessoryFiles/PartFile/Part.csv');
|
|
906
919
|
const rl = csvRecords(filePath);
|
|
@@ -928,7 +941,7 @@ class LoincDataMigrator {
|
|
|
928
941
|
this.updateProgress(remaining);
|
|
929
942
|
}
|
|
930
943
|
|
|
931
|
-
if (options.verbose)
|
|
944
|
+
if (options.verbose) terminal.log(` Processed ${processedCount} parts`);
|
|
932
945
|
}
|
|
933
946
|
|
|
934
947
|
async processPartItem(db, items) {
|
|
@@ -952,7 +965,7 @@ class LoincDataMigrator {
|
|
|
952
965
|
}
|
|
953
966
|
|
|
954
967
|
async processCodes(db, sourceDir, step, options) {
|
|
955
|
-
if (options.verbose)
|
|
968
|
+
if (options.verbose) terminal.log('Processing Main Codes...');
|
|
956
969
|
|
|
957
970
|
const filePath = path.join(sourceDir, 'LoincTable/Loinc.csv');
|
|
958
971
|
const rl = csvRecords(filePath);
|
|
@@ -980,7 +993,7 @@ class LoincDataMigrator {
|
|
|
980
993
|
this.updateProgress(remaining);
|
|
981
994
|
}
|
|
982
995
|
|
|
983
|
-
if (options.verbose)
|
|
996
|
+
if (options.verbose) terminal.log(` Processed ${processedCount} main codes`);
|
|
984
997
|
}
|
|
985
998
|
|
|
986
999
|
async processCodeItem(db, items) {
|
|
@@ -1002,6 +1015,7 @@ class LoincDataMigrator {
|
|
|
1002
1015
|
const clsCode = this.partNames.get('CLASS.' + items[7]);
|
|
1003
1016
|
if (clsCode && this.codes.has(clsCode)) {
|
|
1004
1017
|
this.addRelationship(db, codeKey, this.codes.get(clsCode).key, this.rels.get('CLASS'));
|
|
1018
|
+
this.#noteAxisPart(codeKey, 'CLASS', this.codes.get(clsCode).key);
|
|
1005
1019
|
}
|
|
1006
1020
|
|
|
1007
1021
|
// Add properties
|
|
@@ -1026,11 +1040,11 @@ class LoincDataMigrator {
|
|
|
1026
1040
|
async processConsumerNames(db, sourceDir, step, options) {
|
|
1027
1041
|
const filePath = path.join(sourceDir, 'AccessoryFiles/ConsumerName/ConsumerName.csv');
|
|
1028
1042
|
if (!fs.existsSync(filePath)) {
|
|
1029
|
-
if (options.verbose)
|
|
1043
|
+
if (options.verbose) terminal.warn(`Consumer names file not found: ${filePath}`);
|
|
1030
1044
|
return;
|
|
1031
1045
|
}
|
|
1032
1046
|
|
|
1033
|
-
if (options.verbose)
|
|
1047
|
+
if (options.verbose) terminal.log('Processing Consumer Names...');
|
|
1034
1048
|
|
|
1035
1049
|
const rl = csvRecords(filePath);
|
|
1036
1050
|
|
|
@@ -1061,11 +1075,11 @@ class LoincDataMigrator {
|
|
|
1061
1075
|
async processLists(db, sourceDir, step, options) {
|
|
1062
1076
|
const filePath = path.join(sourceDir, 'AccessoryFiles/AnswerFile/AnswerList.csv');
|
|
1063
1077
|
if (!fs.existsSync(filePath)) {
|
|
1064
|
-
if (options.verbose)
|
|
1078
|
+
if (options.verbose) terminal.warn(`Answer lists file not found: ${filePath}`);
|
|
1065
1079
|
return;
|
|
1066
1080
|
}
|
|
1067
1081
|
|
|
1068
|
-
if (options.verbose)
|
|
1082
|
+
if (options.verbose) terminal.log('Processing Answer Lists...');
|
|
1069
1083
|
|
|
1070
1084
|
const rl = csvRecords(filePath);
|
|
1071
1085
|
|
|
@@ -1113,6 +1127,12 @@ class LoincDataMigrator {
|
|
|
1113
1127
|
}
|
|
1114
1128
|
|
|
1115
1129
|
const answerCode = removeQuotes(items[6]);
|
|
1130
|
+
if (!answerCode) {
|
|
1131
|
+
// an externally defined answer list (ExtDefinedYN = Y: ICD-10, RxNorm, ...) has one row with
|
|
1132
|
+
// no AnswerStringId. The list is a code; the row names no answer, so there is nothing to add
|
|
1133
|
+
// (it used to become a single Answer code '' shared by all 65 such lists)
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1116
1136
|
let answerCodeKey;
|
|
1117
1137
|
|
|
1118
1138
|
if (this.codes.has(answerCode)) {
|
|
@@ -1141,11 +1161,11 @@ class LoincDataMigrator {
|
|
|
1141
1161
|
async processPartMappings(db, sourceDir, step, options) {
|
|
1142
1162
|
const filePath = path.join(sourceDir, 'AccessoryFiles/PartFile/PartRelatedCodeMapping.csv');
|
|
1143
1163
|
if (!fs.existsSync(filePath)) {
|
|
1144
|
-
if (options.verbose)
|
|
1164
|
+
if (options.verbose) terminal.warn(`Part mapping file not found: ${filePath}`);
|
|
1145
1165
|
return;
|
|
1146
1166
|
}
|
|
1147
1167
|
|
|
1148
|
-
if (options.verbose)
|
|
1168
|
+
if (options.verbose) terminal.log('Processing Part Mappings...');
|
|
1149
1169
|
|
|
1150
1170
|
const rl = csvRecords(filePath);
|
|
1151
1171
|
|
|
@@ -1180,11 +1200,11 @@ class LoincDataMigrator {
|
|
|
1180
1200
|
async processIeeeMappings(db, sourceDir, step, options) {
|
|
1181
1201
|
const filePath = path.join(sourceDir, 'AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv');
|
|
1182
1202
|
if (!fs.existsSync(filePath)) {
|
|
1183
|
-
if (options.verbose)
|
|
1203
|
+
if (options.verbose) terminal.warn(`IEEE mapping file not found: ${filePath}`);
|
|
1184
1204
|
return;
|
|
1185
1205
|
}
|
|
1186
1206
|
|
|
1187
|
-
if (options.verbose)
|
|
1207
|
+
if (options.verbose) terminal.log('Processing IEEE Mappings...');
|
|
1188
1208
|
|
|
1189
1209
|
const rl = csvRecords(filePath);
|
|
1190
1210
|
|
|
@@ -1240,11 +1260,11 @@ class LoincDataMigrator {
|
|
|
1240
1260
|
async processPartLinks(db, sourceDir, step, options) {
|
|
1241
1261
|
const filePath = path.join(sourceDir, 'AccessoryFiles/PartFile/LoincPartLink_Primary.csv');
|
|
1242
1262
|
if (!fs.existsSync(filePath)) {
|
|
1243
|
-
if (options.verbose)
|
|
1263
|
+
if (options.verbose) terminal.warn(`Part links file not found: ${filePath}`);
|
|
1244
1264
|
return;
|
|
1245
1265
|
}
|
|
1246
1266
|
|
|
1247
|
-
if (options.verbose)
|
|
1267
|
+
if (options.verbose) terminal.log('Processing Part Links...');
|
|
1248
1268
|
|
|
1249
1269
|
const rl = csvRecords(filePath);
|
|
1250
1270
|
|
|
@@ -1269,6 +1289,9 @@ class LoincDataMigrator {
|
|
|
1269
1289
|
this.rels.get(relType),
|
|
1270
1290
|
this.statii.get(status) || 0
|
|
1271
1291
|
);
|
|
1292
|
+
if (LINGUISTIC_VARIANT_AXES.includes(relType)) {
|
|
1293
|
+
this.#noteAxisPart(this.codes.get(sourceCode).key, relType, this.codes.get(targetCode).key);
|
|
1294
|
+
}
|
|
1272
1295
|
}
|
|
1273
1296
|
processedCount++;
|
|
1274
1297
|
|
|
@@ -1286,11 +1309,11 @@ class LoincDataMigrator {
|
|
|
1286
1309
|
async processListLinks(db, sourceDir, step, options) {
|
|
1287
1310
|
const filePath = path.join(sourceDir, 'AccessoryFiles/AnswerFile/LoincAnswerListLink.csv');
|
|
1288
1311
|
if (!fs.existsSync(filePath)) {
|
|
1289
|
-
if (options.verbose)
|
|
1312
|
+
if (options.verbose) terminal.warn(`List links file not found: ${filePath}`);
|
|
1290
1313
|
return;
|
|
1291
1314
|
}
|
|
1292
1315
|
|
|
1293
|
-
if (options.verbose)
|
|
1316
|
+
if (options.verbose) terminal.log('Processing List Links...');
|
|
1294
1317
|
|
|
1295
1318
|
const rl = csvRecords(filePath);
|
|
1296
1319
|
|
|
@@ -1343,7 +1366,7 @@ class LoincDataMigrator {
|
|
|
1343
1366
|
throw new Error(`Required file missing: ${filePath}`);
|
|
1344
1367
|
}
|
|
1345
1368
|
|
|
1346
|
-
if (options.verbose)
|
|
1369
|
+
if (options.verbose) terminal.log('Processing Hierarchy...');
|
|
1347
1370
|
|
|
1348
1371
|
const rl = csvRecords(filePath);
|
|
1349
1372
|
|
|
@@ -1407,7 +1430,7 @@ class LoincDataMigrator {
|
|
|
1407
1430
|
}
|
|
1408
1431
|
|
|
1409
1432
|
async processPropertyValues(db, step, options) {
|
|
1410
|
-
if (options.verbose)
|
|
1433
|
+
if (options.verbose) terminal.log('Processing Property Values...');
|
|
1411
1434
|
|
|
1412
1435
|
for (const [value, key] of this.propValues) {
|
|
1413
1436
|
db.run('INSERT INTO PropertyValues (PropertyValueKey, Value) VALUES (?, ?)', [key, value]);
|
|
@@ -1415,7 +1438,7 @@ class LoincDataMigrator {
|
|
|
1415
1438
|
}
|
|
1416
1439
|
|
|
1417
1440
|
async storeClosureTable(db, step, options) {
|
|
1418
|
-
if (options.verbose)
|
|
1441
|
+
if (options.verbose) terminal.log('Storing Closure Table...');
|
|
1419
1442
|
|
|
1420
1443
|
let count = 0;
|
|
1421
1444
|
for (const codeInfo of this.codeList) {
|
|
@@ -1440,7 +1463,7 @@ class LoincDataMigrator {
|
|
|
1440
1463
|
}
|
|
1441
1464
|
|
|
1442
1465
|
async processLanguage(db, sourceDir, step, langCode, options) {
|
|
1443
|
-
if (options.verbose)
|
|
1466
|
+
if (options.verbose) terminal.log(`Processing Language ${langCode}...`);
|
|
1444
1467
|
|
|
1445
1468
|
const langKey = this.langs.get(langCode);
|
|
1446
1469
|
if (!langKey) return;
|
|
@@ -1450,7 +1473,7 @@ class LoincDataMigrator {
|
|
|
1450
1473
|
const filePath = path.join(sourceDir, 'AccessoryFiles/LinguisticVariants', fileName);
|
|
1451
1474
|
|
|
1452
1475
|
if (!fs.existsSync(filePath)) {
|
|
1453
|
-
if (options.verbose)
|
|
1476
|
+
if (options.verbose) terminal.warn(`Language file not found: ${filePath}`);
|
|
1454
1477
|
return;
|
|
1455
1478
|
}
|
|
1456
1479
|
|
|
@@ -1472,6 +1495,24 @@ class LoincDataMigrator {
|
|
|
1472
1495
|
this.addDescription(db, codeKey, langKey, this.dTypes.get('SHORTNAME'), items[8]);
|
|
1473
1496
|
this.addDescription(db, codeKey, langKey, this.dTypes.get('LinguisticVariantDisplayName'), items[11]);
|
|
1474
1497
|
|
|
1498
|
+
// The axis columns (COMPONENT ... CLASS) translate the term's Parts. Each becomes a
|
|
1499
|
+
// PartName designation on the Part the term links to for that axis - for some languages
|
|
1500
|
+
// (ko-KR, most of fr-CA) these are the only translations LOINC has. Each distinct name once
|
|
1501
|
+
const axes = this.axisParts.get(codeKey);
|
|
1502
|
+
if (axes) {
|
|
1503
|
+
LINGUISTIC_VARIANT_AXES.forEach((axis, i) => {
|
|
1504
|
+
const value = items[i + 1] ? items[i + 1].trim() : '';
|
|
1505
|
+
const partKey = axes[axis];
|
|
1506
|
+
if (value && partKey) {
|
|
1507
|
+
const id = partKey + '|' + langKey + '|' + value;
|
|
1508
|
+
if (!this.partNamesSeen.has(id)) {
|
|
1509
|
+
this.partNamesSeen.add(id);
|
|
1510
|
+
this.addDescription(db, partKey, langKey, this.dTypes.get('PartName'), value);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1475
1516
|
processedCount++;
|
|
1476
1517
|
|
|
1477
1518
|
if (processedCount % 100 === 0) {
|
|
@@ -1485,6 +1526,15 @@ class LoincDataMigrator {
|
|
|
1485
1526
|
}
|
|
1486
1527
|
}
|
|
1487
1528
|
|
|
1529
|
+
#noteAxisPart(termKey, axis, partKey) {
|
|
1530
|
+
let axes = this.axisParts.get(termKey);
|
|
1531
|
+
if (!axes) {
|
|
1532
|
+
axes = {};
|
|
1533
|
+
this.axisParts.set(termKey, axes);
|
|
1534
|
+
}
|
|
1535
|
+
axes[axis] = partKey;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1488
1538
|
addDescription(db, codeKey, languageKey, descriptionType, value) {
|
|
1489
1539
|
if (!value) return;
|
|
1490
1540
|
|
|
@@ -1508,6 +1558,14 @@ class LoincDataMigrator {
|
|
|
1508
1558
|
|
|
1509
1559
|
addRelationship(db, sourceKey, targetKey, relationshipType, statusKey = 0) {
|
|
1510
1560
|
if (!relationshipType) return;
|
|
1561
|
+
// each relationship once: the source files repeat some (a part under the same parent on
|
|
1562
|
+
// two paths in ComponentHierarchyBySystem, the same answer list linked more than once)
|
|
1563
|
+
if (!this.relSeen) {
|
|
1564
|
+
this.relSeen = new Set();
|
|
1565
|
+
}
|
|
1566
|
+
const id = relationshipType + '|' + sourceKey + '|' + targetKey;
|
|
1567
|
+
if (this.relSeen.has(id)) return;
|
|
1568
|
+
this.relSeen.add(id);
|
|
1511
1569
|
|
|
1512
1570
|
this.relKey++;
|
|
1513
1571
|
db.run('INSERT INTO Relationships (RelationshipKey, RelationshipTypeKey, SourceKey, TargetKey, StatusKey) VALUES (?, ?, ?, ?, ?)',
|
|
@@ -1528,7 +1586,7 @@ class LoincDataMigrator {
|
|
|
1528
1586
|
return new Promise((resolve) => {
|
|
1529
1587
|
db.close((err) => {
|
|
1530
1588
|
if (err && verbose) {
|
|
1531
|
-
|
|
1589
|
+
terminal.error('Error closing database:', err);
|
|
1532
1590
|
}
|
|
1533
1591
|
resolve();
|
|
1534
1592
|
});
|
|
@@ -1537,6 +1595,10 @@ class LoincDataMigrator {
|
|
|
1537
1595
|
}
|
|
1538
1596
|
|
|
1539
1597
|
// Property name mappings from Pascal code
|
|
1598
|
+
// The axis columns of a LinguisticVariant file, in file order (columns 1 to 7), named as the
|
|
1599
|
+
// term's relationships to its Parts are
|
|
1600
|
+
const LINGUISTIC_VARIANT_AXES = ['COMPONENT', 'PROPERTY', 'TIME_ASPCT', 'SYSTEM', 'SCALE_TYP', 'METHOD_TYP', 'CLASS'];
|
|
1601
|
+
|
|
1540
1602
|
const KNOWN_PROPERTY_NAMES = [
|
|
1541
1603
|
'AskAtOrderEntry', 'AssociatedObservations', 'CHANGE_REASON_PUBLIC', 'CHNG_TYPE', 'CLASS', 'CLASSTYPE', 'COMMON_ORDER_RANK', 'COMMON_TEST_RANK', 'COMPONENT', 'CONSUMER_NAME',
|
|
1542
1604
|
'DefinitionDescription', 'DisplayName', 'EXAMPLE_UCUM_UNITS', 'EXAMPLE_UNITS', 'EXMPL_ANSWERS', 'EXTERNAL_COPYRIGHT_LINK', 'EXTERNAL_COPYRIGHT_NOTICE', 'FORMULA',
|