fhirsmith 0.18.2 → 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 +149 -0
- 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 +132 -59
- package/ips/nationality.js +120 -0
- package/ips/pipeline.js +74 -21
- package/ips/presenter.js +1 -1
- package/ips/readme.md +132 -25
- package/ips/render.js +466 -77
- package/ips/store.js +79 -22
- 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/crawler.js +84 -108
- package/registry/documentation/configuration.md +34 -0
- package/registry/registry-template.html +5 -8
- package/registry/registry.js +5 -4
- 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 +154 -4
- package/tx/closure/closure-store.js +126 -3
- package/tx/cm/cm-database.js +69 -57
- package/tx/cm/cm-package.js +29 -1
- 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 +148 -14
- 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 +185 -66
- 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 +608 -64
- 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 +57 -58
- 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/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 +278 -42
- package/tx/resource-cache-sqlite.js +1050 -0
- package/tx/sct/ecl.js +47 -6
- 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 +166 -13
- 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 +27 -4
- package/tx/vs/vs-vsac.js +648 -100
- 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
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
SnomedRelationshipList, SnomedReferenceSetMembers, SnomedReferenceSetIndex
|
|
13
13
|
} = require('../sct/structures');
|
|
14
14
|
const {SnomedExpressionServices} = require("../sct/expressions");
|
|
15
|
+
const terminal = require('./terminal');
|
|
15
16
|
|
|
16
17
|
// definitionStatusId values from the RF2 concept file
|
|
17
18
|
const SNOMED_DEFINITION_STATUS_PRIMITIVE = 900000000000074008n;
|
|
@@ -106,16 +107,16 @@ class SnomedModule extends BaseTerminologyModule {
|
|
|
106
107
|
} catch (error) {
|
|
107
108
|
this.logError(`Import command failed: ${error.message}`);
|
|
108
109
|
if (options.verbose) {
|
|
109
|
-
|
|
110
|
+
terminal.error(error.stack);
|
|
110
111
|
}
|
|
111
112
|
throw error;
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
async confirmImport(config) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
terminal.log(chalk.cyan(`\n📋 ${this.getName()} Import Configuration:`));
|
|
118
|
+
terminal.log(` Source: ${chalk.white(config.source)}`);
|
|
119
|
+
terminal.log(` Destination: ${chalk.white(config.dest)}`);
|
|
119
120
|
|
|
120
121
|
if (config.edition) {
|
|
121
122
|
const editions = {
|
|
@@ -125,26 +126,26 @@ class SnomedModule extends BaseTerminologyModule {
|
|
|
125
126
|
// ... other editions
|
|
126
127
|
};
|
|
127
128
|
const editionName = editions[config.edition] || `Edition ${config.edition}`;
|
|
128
|
-
|
|
129
|
+
terminal.log(` Edition: ${chalk.white(editionName)} (${config.edition})`);
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
if (config.version) {
|
|
132
|
-
|
|
133
|
+
terminal.log(` Version: ${chalk.white(config.version)}`);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
if (config.uri) {
|
|
136
|
-
|
|
137
|
+
terminal.log(` Version URI: ${chalk.white(config.uri)}`);
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
if (config.language) {
|
|
140
|
-
|
|
141
|
+
terminal.log(` Language: ${chalk.white(config.language)}`);
|
|
141
142
|
}
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
|
|
145
|
+
terminal.log(` Verbose: ${chalk.white(config.verbose ? 'Yes' : 'No')}`);
|
|
145
146
|
|
|
146
147
|
if (config.estimatedDuration) {
|
|
147
|
-
|
|
148
|
+
terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
const { confirmed } = await inquirer.prompt({
|
|
@@ -357,7 +358,7 @@ class SnomedModule extends BaseTerminologyModule {
|
|
|
357
358
|
} catch (error) {
|
|
358
359
|
// Silent fail - auto-detection is best-effort
|
|
359
360
|
if (this.config?.verbose) {
|
|
360
|
-
|
|
361
|
+
terminal.log(`Auto-detection warning: ${error.message}`);
|
|
361
362
|
}
|
|
362
363
|
}
|
|
363
364
|
|
|
@@ -661,7 +662,7 @@ class SnomedModule extends BaseTerminologyModule {
|
|
|
661
662
|
|
|
662
663
|
async runImportWithoutConfigSaving(config) {
|
|
663
664
|
try {
|
|
664
|
-
|
|
665
|
+
terminal.log(chalk.blue.bold(`🏥 Starting ${this.getName()} Import...\n`));
|
|
665
666
|
|
|
666
667
|
// Pre-flight checks
|
|
667
668
|
this.logInfo('Running pre-flight checks...');
|
|
@@ -680,7 +681,7 @@ class SnomedModule extends BaseTerminologyModule {
|
|
|
680
681
|
this.stopProgress();
|
|
681
682
|
this.logError(`${this.getName()} import failed: ${error.message}`);
|
|
682
683
|
if (config.verbose) {
|
|
683
|
-
|
|
684
|
+
terminal.error(error.stack);
|
|
684
685
|
}
|
|
685
686
|
process.exit(1);
|
|
686
687
|
}
|
|
@@ -816,10 +817,10 @@ class SnomedModule extends BaseTerminologyModule {
|
|
|
816
817
|
const stats = await this.validateSnomedDirectory(options.source);
|
|
817
818
|
|
|
818
819
|
this.logSuccess('Directory validation passed');
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
820
|
+
terminal.log(` Concept files: ${stats.conceptFiles}`);
|
|
821
|
+
terminal.log(` Description files: ${stats.descriptionFiles}`);
|
|
822
|
+
terminal.log(` Relationship files: ${stats.relationshipFiles}`);
|
|
823
|
+
terminal.log(` Refset directories: ${stats.refsetDirectories}`);
|
|
823
824
|
|
|
824
825
|
} catch (error) {
|
|
825
826
|
this.logError(`Validation failed: ${error.message}`);
|
|
@@ -843,26 +844,26 @@ class SnomedModule extends BaseTerminologyModule {
|
|
|
843
844
|
const data = await reader.loadSnomedData();
|
|
844
845
|
|
|
845
846
|
this.logSuccess('Cache file status:');
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
847
|
+
terminal.log(` Cache Version: ${data.cacheVersion}`);
|
|
848
|
+
terminal.log(` Version URI: ${data.versionUri}`);
|
|
849
|
+
terminal.log(` Version Date: ${data.versionDate}`);
|
|
850
|
+
terminal.log(` Edition: ${data.edition}`);
|
|
851
|
+
terminal.log(` SNOMED Version: ${data.version}`);
|
|
851
852
|
|
|
852
853
|
// Create structure instances to get counts
|
|
853
854
|
const concepts = new SnomedConceptList(data.concept);
|
|
854
855
|
const descriptions = new SnomedDescriptions(data.desc);
|
|
855
856
|
const relationships = new SnomedRelationshipList(data.rel);
|
|
856
857
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
858
|
+
terminal.log(` Concepts: ${concepts.count().toLocaleString()}`);
|
|
859
|
+
terminal.log(` Descriptions: ${descriptions.count().toLocaleString()}`);
|
|
860
|
+
terminal.log(` Relationships: ${relationships.count().toLocaleString()}`);
|
|
861
|
+
terminal.log(` Active Roots: ${data.activeRoots.length}`);
|
|
862
|
+
terminal.log(` Inactive Roots: ${data.inactiveRoots.length}`);
|
|
862
863
|
|
|
863
864
|
const fileStat = fs.statSync(cachePath);
|
|
864
|
-
|
|
865
|
-
|
|
865
|
+
terminal.log(` File Size: ${(fileStat.size / (1024 * 1024 * 1024)).toFixed(2)} GB`);
|
|
866
|
+
terminal.log(` Last Modified: ${fileStat.mtime.toISOString()}`);
|
|
866
867
|
|
|
867
868
|
} catch (error) {
|
|
868
869
|
this.logError(`Status check failed: ${error.message}`);
|
|
@@ -924,7 +925,7 @@ class SnomedImporterWithProgress {
|
|
|
924
925
|
message += ` (${rate.toLocaleString()} items/sec)`;
|
|
925
926
|
}
|
|
926
927
|
|
|
927
|
-
|
|
928
|
+
terminal.log(message);
|
|
928
929
|
|
|
929
930
|
// Clean up
|
|
930
931
|
this.taskStartTimes.delete(taskName);
|
|
@@ -1161,7 +1162,7 @@ class SnomedImporter {
|
|
|
1161
1162
|
await this.saveCache();
|
|
1162
1163
|
|
|
1163
1164
|
} catch (error) {
|
|
1164
|
-
|
|
1165
|
+
terminal.error('DEBUG: Import failed with error:', error);
|
|
1165
1166
|
throw new Error(`Import failed: ${error.message}`);
|
|
1166
1167
|
}
|
|
1167
1168
|
}
|
|
@@ -1181,7 +1182,7 @@ class SnomedImporter {
|
|
|
1181
1182
|
// extension content is layered on top.
|
|
1182
1183
|
if (this.config.base) {
|
|
1183
1184
|
if (this.config.verbose) {
|
|
1184
|
-
|
|
1185
|
+
terminal.log(`Loading base edition from: ${this.config.base}`);
|
|
1185
1186
|
}
|
|
1186
1187
|
this._scanDirectory(this.config.base, files);
|
|
1187
1188
|
}
|
|
@@ -1232,7 +1233,7 @@ class SnomedImporter {
|
|
|
1232
1233
|
files.concreteValues.push(filePath);
|
|
1233
1234
|
}
|
|
1234
1235
|
} catch (error) {
|
|
1235
|
-
|
|
1236
|
+
terminal.log(`DEBUG: Error reading file ${filePath}: ${error.message}`);
|
|
1236
1237
|
}
|
|
1237
1238
|
}
|
|
1238
1239
|
|
|
@@ -1528,14 +1529,15 @@ class SnomedImporter {
|
|
|
1528
1529
|
const month = parseInt(dateStr.substring(4, 6));
|
|
1529
1530
|
const day = parseInt(dateStr.substring(6, 8));
|
|
1530
1531
|
|
|
1531
|
-
//
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
//
|
|
1535
|
-
const
|
|
1532
|
+
// Days since the Pascal TDateTime epoch (December 30, 1899), counted in UTC. Local dates are
|
|
1533
|
+
// not whole days apart: 1899 local time is the zone's historical mean time (e.g. +9:39:52 in
|
|
1534
|
+
// Melbourne) and today's has daylight saving, so the difference came out a day short in zones
|
|
1535
|
+
// ahead of UTC, and every effectiveTime in a cache built there was a day early
|
|
1536
|
+
const targetDate = Date.UTC(year, month - 1, day);
|
|
1537
|
+
const pascalEpoch = Date.UTC(1899, 11, 30); // Month is 0-based in JS
|
|
1536
1538
|
|
|
1537
1539
|
// Calculate days difference
|
|
1538
|
-
const daysDiff = Math.
|
|
1540
|
+
const daysDiff = Math.round((targetDate - pascalEpoch) / (1000 * 60 * 60 * 24));
|
|
1539
1541
|
|
|
1540
1542
|
// Ensure it fits in 16 bits (0-65535) and is positive
|
|
1541
1543
|
if (daysDiff < 0 || daysDiff > 65535) {
|
|
@@ -1784,7 +1786,7 @@ class SnomedImporter {
|
|
|
1784
1786
|
}
|
|
1785
1787
|
this.identifiers.sort((a, b) => (a.join('|') < b.join('|') ? -1 : 1));
|
|
1786
1788
|
if (this.identifiers.length > 0 || missing > 0) {
|
|
1787
|
-
|
|
1789
|
+
terminal.log(`Alternate identifiers: ${this.identifiers.length}${missing ? ` (${missing} for concepts not in this edition, ignored)` : ''}`);
|
|
1788
1790
|
}
|
|
1789
1791
|
}
|
|
1790
1792
|
|
|
@@ -1836,7 +1838,7 @@ class SnomedImporter {
|
|
|
1836
1838
|
}
|
|
1837
1839
|
this.concreteValues.sort((a, b) => (a.join('|') < b.join('|') ? -1 : 1));
|
|
1838
1840
|
if (this.concreteValues.length > 0 || missing > 0) {
|
|
1839
|
-
|
|
1841
|
+
terminal.log(`Concrete values: ${this.concreteValues.length}${missing ? ` (${missing} for concepts not in this edition, ignored)` : ''}`);
|
|
1840
1842
|
}
|
|
1841
1843
|
}
|
|
1842
1844
|
|
|
@@ -2251,7 +2253,7 @@ class SnomedImporter {
|
|
|
2251
2253
|
|
|
2252
2254
|
async processRefsets() {
|
|
2253
2255
|
if (this.files.refsetDirectories.length === 0) {
|
|
2254
|
-
|
|
2256
|
+
terminal.log('No reference set directories found');
|
|
2255
2257
|
this.refsetIndex.doneBuild();
|
|
2256
2258
|
this.refsetMembers.doneBuild();
|
|
2257
2259
|
return;
|
|
@@ -2367,7 +2369,7 @@ class SnomedImporter {
|
|
|
2367
2369
|
const parts = fileName.split('_');
|
|
2368
2370
|
|
|
2369
2371
|
if (parts.length < 3) {
|
|
2370
|
-
|
|
2372
|
+
terminal.log(`Skipping file with unexpected name format: ${fileName}`);
|
|
2371
2373
|
return;
|
|
2372
2374
|
}
|
|
2373
2375
|
|
|
@@ -2391,19 +2393,16 @@ class SnomedImporter {
|
|
|
2391
2393
|
lineNumber++;
|
|
2392
2394
|
|
|
2393
2395
|
if (lineNumber === 1) {
|
|
2394
|
-
// Parse headers
|
|
2396
|
+
// Parse headers. Map reference sets (simple, complex, extended) are loaded like
|
|
2397
|
+
// any other: ECL ^ and ?fhir_vs=refset/ can name any reference set, and the
|
|
2398
|
+
// ICD-10 maps are among the most used.
|
|
2395
2399
|
headers = line.split('\t').map(h => h.trim());
|
|
2396
|
-
|
|
2397
|
-
// Skip if this looks like a mapping reference set
|
|
2398
|
-
if (line.toLowerCase().includes('map')) {
|
|
2399
|
-
return;
|
|
2400
|
-
}
|
|
2401
2400
|
continue;
|
|
2402
2401
|
}
|
|
2403
2402
|
|
|
2404
2403
|
const fields = line.split('\t');
|
|
2405
2404
|
if (fields.length < 6) {
|
|
2406
|
-
|
|
2405
|
+
terminal.log(`Skipping line ${lineNumber} with insufficient fields: ${fields.length}`);
|
|
2407
2406
|
continue; // Minimum fields required
|
|
2408
2407
|
}
|
|
2409
2408
|
|
|
@@ -2439,14 +2438,14 @@ class SnomedImporter {
|
|
|
2439
2438
|
try {
|
|
2440
2439
|
member.id = this.parseGUID(id);
|
|
2441
2440
|
} catch (error) {
|
|
2442
|
-
|
|
2441
|
+
terminal.log(`Invalid GUID in ${fileName}: ${id}`);
|
|
2443
2442
|
continue;
|
|
2444
2443
|
}
|
|
2445
2444
|
|
|
2446
2445
|
// Find module concept
|
|
2447
2446
|
const moduleConcept = this.conceptMap.get(BigInt(moduleId));
|
|
2448
2447
|
if (!moduleConcept) {
|
|
2449
|
-
|
|
2448
|
+
terminal.log(`Module concept not found: ${moduleId}`);
|
|
2450
2449
|
continue;
|
|
2451
2450
|
}
|
|
2452
2451
|
member.module = moduleConcept.index;
|
|
@@ -2479,7 +2478,7 @@ class SnomedImporter {
|
|
|
2479
2478
|
// Try to find as relationship (simplified - would need relationship ID lookup)
|
|
2480
2479
|
member.kind = 3; // Other/unknown
|
|
2481
2480
|
member.ref = 0;
|
|
2482
|
-
|
|
2481
|
+
terminal.log(`Component not found: ${referencedComponentId}`);
|
|
2483
2482
|
continue;
|
|
2484
2483
|
}
|
|
2485
2484
|
}
|
|
@@ -2502,7 +2501,7 @@ class SnomedImporter {
|
|
|
2502
2501
|
}
|
|
2503
2502
|
|
|
2504
2503
|
} catch (error) {
|
|
2505
|
-
|
|
2504
|
+
terminal.error(`Error processing reference set ${filePath}:`, error);
|
|
2506
2505
|
}
|
|
2507
2506
|
}
|
|
2508
2507
|
|
|
@@ -2590,7 +2589,7 @@ class SnomedImporter {
|
|
|
2590
2589
|
value = descResult.index;
|
|
2591
2590
|
type = 2; // Description
|
|
2592
2591
|
} else {
|
|
2593
|
-
|
|
2592
|
+
terminal.log(`Referenced component not found: ${field}`);
|
|
2594
2593
|
value = 0;
|
|
2595
2594
|
type = 1;
|
|
2596
2595
|
}
|
|
@@ -2847,7 +2846,7 @@ class SnomedImporter {
|
|
|
2847
2846
|
} catch (error) {
|
|
2848
2847
|
// Log the error but continue processing other concepts
|
|
2849
2848
|
if (this.config.verbose) {
|
|
2850
|
-
|
|
2849
|
+
terminal.warn(`Warning: Could not build normal form for concept ${concept.id}: ${error.message}`);
|
|
2851
2850
|
}
|
|
2852
2851
|
}
|
|
2853
2852
|
|
|
@@ -2862,7 +2861,7 @@ class SnomedImporter {
|
|
|
2862
2861
|
}
|
|
2863
2862
|
|
|
2864
2863
|
if (this.config.verbose) {
|
|
2865
|
-
|
|
2864
|
+
terminal.log(`Normal forms: ${normalFormsAdded} concepts have non-trivial normal forms`);
|
|
2866
2865
|
}
|
|
2867
2866
|
}
|
|
2868
2867
|
|
|
@@ -3,6 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const sqlite3 = require('sqlite3').verbose();
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const readline = require("readline");
|
|
6
|
+
const terminal = require('./terminal');
|
|
6
7
|
|
|
7
8
|
class UniiModule extends BaseTerminologyModule {
|
|
8
9
|
getName() {
|
|
@@ -81,16 +82,16 @@ class UniiModule extends BaseTerminologyModule {
|
|
|
81
82
|
const inquirer = require('inquirer');
|
|
82
83
|
const chalk = require('chalk');
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
terminal.log(chalk.cyan(`\n📋 ${this.getName()} Import Configuration:`));
|
|
86
|
+
terminal.log(` Source: ${chalk.white(config.source)}`);
|
|
87
|
+
terminal.log(` Destination: ${chalk.white(config.dest)}`);
|
|
88
|
+
terminal.log(` Version: ${chalk.white(config.version)}`);
|
|
89
|
+
terminal.log(` Create Indexes: ${chalk.white(config.createIndexes ? 'Yes' : 'No')}`);
|
|
90
|
+
terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
|
|
91
|
+
terminal.log(` Verbose: ${chalk.white(config.verbose ? 'Yes' : 'No')}`);
|
|
91
92
|
|
|
92
93
|
if (config.estimatedDuration) {
|
|
93
|
-
|
|
94
|
+
terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
const { confirmed } = await inquirer.prompt({
|
|
@@ -120,14 +121,14 @@ class UniiModule extends BaseTerminologyModule {
|
|
|
120
121
|
const stats = await this.validateUniiFile(options.source, parseInt(options.sample));
|
|
121
122
|
|
|
122
123
|
this.logSuccess('File validation passed');
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
terminal.log(` Lines: ${stats.totalLines}`);
|
|
125
|
+
terminal.log(` Estimated UNII codes: ${stats.estimatedCodes}`);
|
|
126
|
+
terminal.log(` Estimated descriptions: ${stats.estimatedDescriptions}`);
|
|
127
|
+
terminal.log(` Sample data format: ${stats.formatValid ? 'Valid' : 'Invalid'}`);
|
|
127
128
|
|
|
128
129
|
if (stats.warnings.length > 0) {
|
|
129
130
|
this.logWarning('Validation warnings:');
|
|
130
|
-
stats.warnings.forEach(warning =>
|
|
131
|
+
stats.warnings.forEach(warning => terminal.log(` ${warning}`));
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
} catch (error) {
|
|
@@ -149,11 +150,11 @@ class UniiModule extends BaseTerminologyModule {
|
|
|
149
150
|
const stats = await this.getDatabaseStats(dbPath);
|
|
150
151
|
|
|
151
152
|
this.logSuccess('Database status:');
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
terminal.log(` Version: ${stats.version}`);
|
|
154
|
+
terminal.log(` UNII Codes: ${stats.uniiCount.toLocaleString()}`);
|
|
155
|
+
terminal.log(` Descriptions: ${stats.descCount.toLocaleString()}`);
|
|
156
|
+
terminal.log(` Database Size: ${stats.sizeGB.toFixed(2)} GB`);
|
|
157
|
+
terminal.log(` Last Modified: ${stats.lastModified}`);
|
|
157
158
|
|
|
158
159
|
} catch (error) {
|
|
159
160
|
this.logError(`Status check failed: ${error.message}`);
|
|
@@ -304,7 +305,7 @@ class UniiModule extends BaseTerminologyModule {
|
|
|
304
305
|
db.serialize(() => {
|
|
305
306
|
indexes.forEach(sql => {
|
|
306
307
|
db.run(sql, (err) => {
|
|
307
|
-
if (err)
|
|
308
|
+
if (err) terminal.warn(`Index creation warning: ${err.message}`);
|
|
308
309
|
});
|
|
309
310
|
});
|
|
310
311
|
});
|
|
@@ -327,7 +328,7 @@ class UniiDataMigrator {
|
|
|
327
328
|
* @returns {Promise<void>}
|
|
328
329
|
*/
|
|
329
330
|
async migrate(sourceFile, destFile, version = 'unknown', verbose = true) {
|
|
330
|
-
if (verbose)
|
|
331
|
+
if (verbose) terminal.log('Starting UNII data migration...');
|
|
331
332
|
|
|
332
333
|
// Ensure destination directory exists
|
|
333
334
|
const destDir = path.dirname(destFile);
|
|
@@ -350,7 +351,7 @@ class UniiDataMigrator {
|
|
|
350
351
|
// Process source file
|
|
351
352
|
await this.#processSourceFile(db, sourceFile, verbose);
|
|
352
353
|
|
|
353
|
-
if (verbose)
|
|
354
|
+
if (verbose) terminal.log('UNII data migration completed successfully');
|
|
354
355
|
} finally {
|
|
355
356
|
await this.#closeDatabase(db, verbose);
|
|
356
357
|
}
|
|
@@ -361,7 +362,7 @@ class UniiDataMigrator {
|
|
|
361
362
|
* @private
|
|
362
363
|
*/
|
|
363
364
|
async #createTables(db, version, verbose = true) {
|
|
364
|
-
if (verbose)
|
|
365
|
+
if (verbose) terminal.log('Creating database tables...');
|
|
365
366
|
|
|
366
367
|
return new Promise((resolve, reject) => {
|
|
367
368
|
db.serialize(() => {
|
|
@@ -400,7 +401,7 @@ class UniiDataMigrator {
|
|
|
400
401
|
// Insert version
|
|
401
402
|
db.run('INSERT INTO UniiVersion (Version) VALUES (?)', [version], (err) => {
|
|
402
403
|
if (err) return reject(err);
|
|
403
|
-
if (verbose)
|
|
404
|
+
if (verbose) terminal.log('Database tables created');
|
|
404
405
|
resolve();
|
|
405
406
|
});
|
|
406
407
|
});
|
|
@@ -412,7 +413,7 @@ class UniiDataMigrator {
|
|
|
412
413
|
* @private
|
|
413
414
|
*/
|
|
414
415
|
async #processSourceFile(db, sourceFile, verbose = true) {
|
|
415
|
-
if (verbose)
|
|
416
|
+
if (verbose) terminal.log('Processing source file:', sourceFile);
|
|
416
417
|
|
|
417
418
|
if (!fs.existsSync(sourceFile)) {
|
|
418
419
|
throw new Error(`Source file not found: ${sourceFile}`);
|
|
@@ -438,7 +439,7 @@ class UniiDataMigrator {
|
|
|
438
439
|
throw new Error('Source file is empty');
|
|
439
440
|
}
|
|
440
441
|
|
|
441
|
-
if (verbose)
|
|
442
|
+
if (verbose) terminal.log(`Read ${lines.length} lines, processing...`);
|
|
442
443
|
|
|
443
444
|
// Track processed codes and auto-increment keys
|
|
444
445
|
const codeMap = new Map(); // code -> UniiKey
|
|
@@ -467,7 +468,7 @@ class UniiDataMigrator {
|
|
|
467
468
|
|
|
468
469
|
// Need at least 3 columns (Display, Type, Code)
|
|
469
470
|
if (cols.length < 3) {
|
|
470
|
-
if (verbose)
|
|
471
|
+
if (verbose) terminal.warn(`Skipping line ${i + 1}: insufficient columns`);
|
|
471
472
|
continue;
|
|
472
473
|
}
|
|
473
474
|
|
|
@@ -476,7 +477,7 @@ class UniiDataMigrator {
|
|
|
476
477
|
const code = cols[2] || '';
|
|
477
478
|
|
|
478
479
|
if (!code) {
|
|
479
|
-
if (verbose)
|
|
480
|
+
if (verbose) terminal.warn(`Skipping line ${i + 1}: empty UNII code`);
|
|
480
481
|
continue;
|
|
481
482
|
}
|
|
482
483
|
|
|
@@ -510,14 +511,14 @@ class UniiDataMigrator {
|
|
|
510
511
|
});
|
|
511
512
|
|
|
512
513
|
if (processedLines % 10000 === 0) {
|
|
513
|
-
|
|
514
|
+
terminal.log(`Processed ${processedLines} lines...`);
|
|
514
515
|
}
|
|
515
516
|
}
|
|
516
517
|
|
|
517
518
|
if (verbose) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
519
|
+
terminal.log(`Processing completed. Total lines processed: ${processedLines}`);
|
|
520
|
+
terminal.log(`Unique UNII codes: ${codeMap.size}`);
|
|
521
|
+
terminal.log(`Total descriptions: ${lastUniiDescKey}`);
|
|
521
522
|
}
|
|
522
523
|
}
|
|
523
524
|
|
|
@@ -529,7 +530,7 @@ class UniiDataMigrator {
|
|
|
529
530
|
return new Promise((resolve) => {
|
|
530
531
|
db.close((err) => {
|
|
531
532
|
if (err && verbose) {
|
|
532
|
-
|
|
533
|
+
terminal.error('Error closing database:', err);
|
|
533
534
|
}
|
|
534
535
|
resolve();
|
|
535
536
|
});
|
|
@@ -558,8 +559,8 @@ class UniiDataMigratorWithProgress {
|
|
|
558
559
|
let processedLines = 0;
|
|
559
560
|
|
|
560
561
|
// Override the progress reporting in the original migrator
|
|
561
|
-
const
|
|
562
|
-
|
|
562
|
+
const originalLog = terminal.log;
|
|
563
|
+
terminal.log = (...args) => {
|
|
563
564
|
const message = args.join(' ');
|
|
564
565
|
if (message.includes('Processed') && message.includes('lines')) {
|
|
565
566
|
const match = message.match(/Processed (\d+) lines/);
|
|
@@ -568,13 +569,13 @@ class UniiDataMigratorWithProgress {
|
|
|
568
569
|
this.module.updateProgress(processedLines);
|
|
569
570
|
}
|
|
570
571
|
}
|
|
571
|
-
if (verbose)
|
|
572
|
+
if (verbose) originalLog(...args);
|
|
572
573
|
};
|
|
573
574
|
|
|
574
575
|
try {
|
|
575
576
|
await originalMigrator.migrate(sourceFile, destFile, version, verbose);
|
|
576
577
|
} finally {
|
|
577
|
-
|
|
578
|
+
terminal.log = originalLog;
|
|
578
579
|
this.module.stopProgress();
|
|
579
580
|
}
|
|
580
581
|
}
|