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
|
@@ -4,6 +4,7 @@ const path = require('path');
|
|
|
4
4
|
const readline = require('readline');
|
|
5
5
|
const { csvRecords } = require('./csv-records');
|
|
6
6
|
const chalk = require('chalk');
|
|
7
|
+
const terminal = require('./terminal');
|
|
7
8
|
|
|
8
9
|
class LoincSubsetModule extends BaseTerminologyModule {
|
|
9
10
|
constructor() {
|
|
@@ -84,7 +85,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
84
85
|
} catch (error) {
|
|
85
86
|
this.logError(`Subset operation failed: ${error.message}`);
|
|
86
87
|
if (options.verbose) {
|
|
87
|
-
|
|
88
|
+
terminal.error(error.stack);
|
|
88
89
|
}
|
|
89
90
|
throw error;
|
|
90
91
|
}
|
|
@@ -220,15 +221,15 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
async confirmSubset(config) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
224
|
+
terminal.log(chalk.cyan(`\nš LOINC Subset Configuration:`));
|
|
225
|
+
terminal.log(` Source: ${chalk.white(config.source)}`);
|
|
226
|
+
terminal.log(` Destination: ${chalk.white(config.dest)}`);
|
|
227
|
+
terminal.log(` Codes File: ${chalk.white(config.codes)}`);
|
|
228
|
+
terminal.log(` Expand PartLinks: ${chalk.white(config.expandPartLinks ? 'Yes' : 'No')}`);
|
|
229
|
+
terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
|
|
229
230
|
|
|
230
231
|
if (config.estimatedDuration) {
|
|
231
|
-
|
|
232
|
+
terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
|
|
232
233
|
}
|
|
233
234
|
|
|
234
235
|
const { confirmed } = await require('inquirer').prompt({
|
|
@@ -243,14 +244,14 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
243
244
|
|
|
244
245
|
async runSubset(config) {
|
|
245
246
|
try {
|
|
246
|
-
|
|
247
|
+
terminal.log(chalk.blue.bold(`š¬ Starting LOINC Subset Creation...\n`));
|
|
247
248
|
|
|
248
249
|
if (config.verbose) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
250
|
+
terminal.log('Debug - Final config values:');
|
|
251
|
+
terminal.log(` Source: ${config.source}`);
|
|
252
|
+
terminal.log(` Dest: ${config.dest}`);
|
|
253
|
+
terminal.log(` Codes: ${config.codes}`);
|
|
254
|
+
terminal.log('');
|
|
254
255
|
}
|
|
255
256
|
|
|
256
257
|
// Pre-flight checks
|
|
@@ -270,7 +271,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
270
271
|
this.stopProgress();
|
|
271
272
|
this.logError(`LOINC subset creation failed: ${error.message}`);
|
|
272
273
|
if (config.verbose) {
|
|
273
|
-
|
|
274
|
+
terminal.error(error.stack);
|
|
274
275
|
}
|
|
275
276
|
throw error;
|
|
276
277
|
}
|
|
@@ -303,13 +304,13 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
303
304
|
const stats = await this.validateSubsetInputs(options.source, options.codes);
|
|
304
305
|
|
|
305
306
|
this.logSuccess('Validation passed');
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
terminal.log(` Source files found: ${stats.filesFound.length}`);
|
|
308
|
+
terminal.log(` Codes in list: ${stats.codeCount.toLocaleString()}`);
|
|
309
|
+
terminal.log(` Unique codes: ${stats.uniqueCodes.toLocaleString()}`);
|
|
309
310
|
|
|
310
311
|
if (stats.warnings.length > 0) {
|
|
311
312
|
this.logWarning('Validation warnings:');
|
|
312
|
-
stats.warnings.forEach(warning =>
|
|
313
|
+
stats.warnings.forEach(warning => terminal.log(` ${warning}`));
|
|
313
314
|
}
|
|
314
315
|
|
|
315
316
|
} catch (error) {
|
|
@@ -324,7 +325,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
324
325
|
check: () => {
|
|
325
326
|
const exists = fs.existsSync(config.source);
|
|
326
327
|
if (!exists && config.verbose) {
|
|
327
|
-
|
|
328
|
+
terminal.log(` Source path being checked: ${config.source}`);
|
|
328
329
|
}
|
|
329
330
|
return exists;
|
|
330
331
|
}
|
|
@@ -334,7 +335,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
334
335
|
check: () => {
|
|
335
336
|
const exists = fs.existsSync(config.codes);
|
|
336
337
|
if (!exists && config.verbose) {
|
|
337
|
-
|
|
338
|
+
terminal.log(` Codes path being checked: ${config.codes}`);
|
|
338
339
|
}
|
|
339
340
|
return exists;
|
|
340
341
|
}
|
|
@@ -351,7 +352,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
351
352
|
const fullPath = path.join(config.source, file);
|
|
352
353
|
const exists = fs.existsSync(fullPath);
|
|
353
354
|
if (!exists && config.verbose) {
|
|
354
|
-
|
|
355
|
+
terminal.log(` Missing required file: ${fullPath}`);
|
|
355
356
|
}
|
|
356
357
|
return exists;
|
|
357
358
|
});
|
|
@@ -390,14 +391,14 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
390
391
|
|
|
391
392
|
if (config.verbose) {
|
|
392
393
|
const sampleCodes = Array.from(initialTargetCodes).slice(0, 20);
|
|
393
|
-
|
|
394
|
+
terminal.log(`First 20 codes from file: ${sampleCodes.join(', ')}`);
|
|
394
395
|
|
|
395
396
|
// Validate some codes exist in the main LOINC file
|
|
396
397
|
this.logInfo('Validating codes exist in LOINC...');
|
|
397
398
|
const validationResults = await this.validateCodesExist(config.source, initialTargetCodes);
|
|
398
|
-
|
|
399
|
+
terminal.log(`Found ${validationResults.found} of ${validationResults.checked} codes in LOINC main table`);
|
|
399
400
|
if (validationResults.notFound.length > 0) {
|
|
400
|
-
|
|
401
|
+
terminal.log(`Sample missing codes: ${validationResults.notFound.slice(0, 5).join(', ')}`);
|
|
401
402
|
}
|
|
402
403
|
}
|
|
403
404
|
|
|
@@ -414,7 +415,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
414
415
|
if (config.verbose && addedCodes > 0) {
|
|
415
416
|
const newCodes = Array.from(finalTargetCodes).filter(code => !initialTargetCodes.has(code));
|
|
416
417
|
const sampleNewCodes = newCodes.slice(0, 10);
|
|
417
|
-
|
|
418
|
+
terminal.log(`Sample newly added codes: ${sampleNewCodes.join(', ')}`);
|
|
418
419
|
}
|
|
419
420
|
} else {
|
|
420
421
|
this.logInfo('Skipping PartLink expansion (disabled)');
|
|
@@ -449,7 +450,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
449
450
|
|
|
450
451
|
if (!fs.existsSync(partLinkPath)) {
|
|
451
452
|
if (verbose) {
|
|
452
|
-
|
|
453
|
+
terminal.log(` PartLink file not found: ${partLinkPath}`);
|
|
453
454
|
}
|
|
454
455
|
return new Set(initialCodes);
|
|
455
456
|
}
|
|
@@ -457,8 +458,8 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
457
458
|
const expandedCodes = new Set(initialCodes);
|
|
458
459
|
|
|
459
460
|
if (verbose) {
|
|
460
|
-
|
|
461
|
-
|
|
461
|
+
terminal.log(` Processing PartLink relationships (single pass)...`);
|
|
462
|
+
terminal.log(` Original codes count: ${initialCodes.size}`);
|
|
462
463
|
}
|
|
463
464
|
|
|
464
465
|
const rl = readline.createInterface({
|
|
@@ -495,19 +496,19 @@ class LoincSubsetModule extends BaseTerminologyModule {
|
|
|
495
496
|
addedCodes += (sizeAfter - sizeBefore);
|
|
496
497
|
|
|
497
498
|
if (verbose && matchedLines <= 10) {
|
|
498
|
-
|
|
499
|
+
terminal.log(` Match ${matchedLines}: "${firstCode}" (${firstInOriginal ? 'in original' : 'new'}) <-> "${thirdCode}" (${thirdInOriginal ? 'in original' : 'new'})`);
|
|
499
500
|
}
|
|
500
501
|
}
|
|
501
502
|
}
|
|
502
503
|
|
|
503
504
|
if (verbose) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
505
|
+
terminal.log(` Single pass completed: ${matchedLines} matching relationships found`);
|
|
506
|
+
terminal.log(` Added ${addedCodes} new codes`);
|
|
507
|
+
terminal.log(` Final expanded codes count: ${expandedCodes.size}`);
|
|
507
508
|
|
|
508
509
|
// Show what percentage this represents
|
|
509
510
|
const expansionRatio = expandedCodes.size / initialCodes.size;
|
|
510
|
-
|
|
511
|
+
terminal.log(` Expansion ratio: ${expansionRatio.toFixed(1)}x original size`);
|
|
511
512
|
}
|
|
512
513
|
|
|
513
514
|
return expandedCodes;
|
|
@@ -837,12 +838,12 @@ class LoincSubsetProcessor {
|
|
|
837
838
|
|
|
838
839
|
async processParts(sourcePath, destPath) {
|
|
839
840
|
if (this.verbose) {
|
|
840
|
-
|
|
841
|
-
|
|
841
|
+
terminal.log(` Processing parts file: ${sourcePath}`);
|
|
842
|
+
terminal.log(` Target codes size: ${this.targetCodes.size}`);
|
|
842
843
|
|
|
843
844
|
// Show first few target codes for comparison
|
|
844
845
|
const sampleCodes = Array.from(this.targetCodes).slice(0, 5);
|
|
845
|
-
|
|
846
|
+
terminal.log(` Sample target codes: ${sampleCodes.join(', ')}`);
|
|
846
847
|
}
|
|
847
848
|
|
|
848
849
|
await this.processFileWithFilter(sourcePath, destPath, (line, lineNum) => {
|
|
@@ -856,7 +857,7 @@ class LoincSubsetProcessor {
|
|
|
856
857
|
|
|
857
858
|
// Debug first few lines
|
|
858
859
|
if (this.verbose && lineNum <= 5) {
|
|
859
|
-
|
|
860
|
+
terminal.log(` Line ${lineNum}: raw="${items[0]}", clean="${code}", match=${hasCode}`);
|
|
860
861
|
}
|
|
861
862
|
|
|
862
863
|
return hasCode;
|
|
@@ -865,12 +866,12 @@ class LoincSubsetProcessor {
|
|
|
865
866
|
|
|
866
867
|
async processConsumerNames(sourcePath, destPath) {
|
|
867
868
|
if (this.verbose) {
|
|
868
|
-
|
|
869
|
-
|
|
869
|
+
terminal.log(` Processing consumer names file: ${sourcePath}`);
|
|
870
|
+
terminal.log(` Target codes size: ${this.targetCodes.size}`);
|
|
870
871
|
|
|
871
872
|
// Show first few target codes for comparison
|
|
872
873
|
const sampleCodes = Array.from(this.targetCodes).slice(0, 5);
|
|
873
|
-
|
|
874
|
+
terminal.log(` Sample target codes: ${sampleCodes.join(', ')}`);
|
|
874
875
|
}
|
|
875
876
|
|
|
876
877
|
await this.processFileWithFilter(sourcePath, destPath, (line, lineNum) => {
|
|
@@ -884,7 +885,7 @@ class LoincSubsetProcessor {
|
|
|
884
885
|
|
|
885
886
|
// Debug first few lines
|
|
886
887
|
if (this.verbose && lineNum <= 5) {
|
|
887
|
-
|
|
888
|
+
terminal.log(` Line ${lineNum}: raw="${items[0]}", clean="${code}", match=${hasCode}`);
|
|
888
889
|
}
|
|
889
890
|
|
|
890
891
|
return hasCode;
|
|
@@ -917,10 +918,10 @@ class LoincSubsetProcessor {
|
|
|
917
918
|
|
|
918
919
|
async processPartLinks(sourcePath, destPath) {
|
|
919
920
|
if (this.verbose) {
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
921
|
+
terminal.log(` Processing part links file: ${sourcePath}`);
|
|
922
|
+
terminal.log(` Target codes size: ${this.targetCodes.size}`);
|
|
923
|
+
terminal.log(` Original codes size: ${this.originalCodes.size}`);
|
|
924
|
+
terminal.log(` Using ORIGINAL codes for PartLink filtering to prevent expansion explosion`);
|
|
924
925
|
}
|
|
925
926
|
|
|
926
927
|
let debugCount = 0;
|
|
@@ -941,7 +942,7 @@ class LoincSubsetProcessor {
|
|
|
941
942
|
// Debug first few included lines
|
|
942
943
|
if (this.verbose && shouldInclude && debugCount < 10) {
|
|
943
944
|
debugCount++;
|
|
944
|
-
|
|
945
|
+
terminal.log(` Include ${debugCount}: "${sourceCode}" (${hasSource ? 'original' : 'no'}) <-> "${targetCode}" (${hasTarget ? 'original' : 'no'})`);
|
|
945
946
|
}
|
|
946
947
|
|
|
947
948
|
return shouldInclude;
|
|
@@ -1051,7 +1052,7 @@ class LoincSubsetProcessor {
|
|
|
1051
1052
|
writeStream.end();
|
|
1052
1053
|
|
|
1053
1054
|
if (this.verbose && lineNum > 1) {
|
|
1054
|
-
|
|
1055
|
+
terminal.log(` Included ${includedLines - 1} of ${lineNum - 1} data rows`);
|
|
1055
1056
|
}
|
|
1056
1057
|
}
|
|
1057
1058
|
|
|
@@ -3,6 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const readline = require('readline');
|
|
5
5
|
const chalk = require('chalk');
|
|
6
|
+
const terminal = require('./terminal');
|
|
6
7
|
|
|
7
8
|
class RxNormSubsetModule extends BaseTerminologyModule {
|
|
8
9
|
constructor() {
|
|
@@ -86,7 +87,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
|
|
|
86
87
|
} catch (error) {
|
|
87
88
|
this.logError(`Subset operation failed: ${error.message}`);
|
|
88
89
|
if (options.verbose) {
|
|
89
|
-
|
|
90
|
+
terminal.error(error.stack);
|
|
90
91
|
}
|
|
91
92
|
throw error;
|
|
92
93
|
}
|
|
@@ -237,18 +238,18 @@ class RxNormSubsetModule extends BaseTerminologyModule {
|
|
|
237
238
|
}
|
|
238
239
|
|
|
239
240
|
async confirmSubset(config) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
241
|
+
terminal.log(chalk.cyan(`\nš RxNorm Subset Configuration:`));
|
|
242
|
+
terminal.log(` Source: ${chalk.white(config.source)}`);
|
|
243
|
+
terminal.log(` Destination: ${chalk.white(config.dest)}`);
|
|
244
|
+
terminal.log(` Codes File: ${chalk.white(config.codes)}`);
|
|
245
|
+
terminal.log(` Expand Relationships: ${chalk.white(config.expandRelationships ? 'Yes' : 'No')}`);
|
|
246
|
+
terminal.log(` Include Synonyms: ${chalk.white(config.includeSynonyms ? 'Yes' : 'No')}`);
|
|
247
|
+
terminal.log(` Include Archived: ${chalk.white(config.includeArchived ? 'Yes' : 'No')}`);
|
|
248
|
+
terminal.log(` Max Iterations: ${chalk.white(config.maxIterations)}`);
|
|
249
|
+
terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
|
|
249
250
|
|
|
250
251
|
if (config.estimatedDuration) {
|
|
251
|
-
|
|
252
|
+
terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
|
|
252
253
|
}
|
|
253
254
|
|
|
254
255
|
const { confirmed } = await require('inquirer').prompt({
|
|
@@ -263,7 +264,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
|
|
|
263
264
|
|
|
264
265
|
async runSubset(config) {
|
|
265
266
|
try {
|
|
266
|
-
|
|
267
|
+
terminal.log(chalk.blue.bold(`𧬠Starting RxNorm Subset Creation...\n`));
|
|
267
268
|
|
|
268
269
|
// Pre-flight checks
|
|
269
270
|
this.logInfo('Running pre-flight checks...');
|
|
@@ -282,7 +283,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
|
|
|
282
283
|
this.stopProgress();
|
|
283
284
|
this.logError(`RxNorm subset creation failed: ${error.message}`);
|
|
284
285
|
if (config.verbose) {
|
|
285
|
-
|
|
286
|
+
terminal.error(error.stack);
|
|
286
287
|
}
|
|
287
288
|
throw error;
|
|
288
289
|
}
|
|
@@ -315,14 +316,14 @@ class RxNormSubsetModule extends BaseTerminologyModule {
|
|
|
315
316
|
const stats = await this.validateSubsetInputs(options.source, options.codes);
|
|
316
317
|
|
|
317
318
|
this.logSuccess('Validation passed');
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
319
|
+
terminal.log(` Required files found: ${stats.requiredFiles.length}/3`);
|
|
320
|
+
terminal.log(` Optional files found: ${stats.optionalFiles.length}/3`);
|
|
321
|
+
terminal.log(` Codes in list: ${stats.codeCount.toLocaleString()}`);
|
|
322
|
+
terminal.log(` Unique codes: ${stats.uniqueCodes.toLocaleString()}`);
|
|
322
323
|
|
|
323
324
|
if (stats.warnings.length > 0) {
|
|
324
325
|
this.logWarning('Validation warnings:');
|
|
325
|
-
stats.warnings.forEach(warning =>
|
|
326
|
+
stats.warnings.forEach(warning => terminal.log(` ${warning}`));
|
|
326
327
|
}
|
|
327
328
|
|
|
328
329
|
} catch (error) {
|
|
@@ -380,7 +381,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
|
|
|
380
381
|
|
|
381
382
|
if (config.verbose) {
|
|
382
383
|
const sampleCodes = Array.from(initialTargetCodes).slice(0, 10);
|
|
383
|
-
|
|
384
|
+
terminal.log(`Sample codes: ${sampleCodes.join(', ')}`);
|
|
384
385
|
}
|
|
385
386
|
|
|
386
387
|
let finalTargetCodes = initialTargetCodes;
|
|
@@ -403,7 +404,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
|
|
|
403
404
|
if (config.verbose && addedCodes > 0) {
|
|
404
405
|
const newCodes = Array.from(finalTargetCodes).filter(code => !initialTargetCodes.has(code));
|
|
405
406
|
const sampleNewCodes = newCodes.slice(0, 10);
|
|
406
|
-
|
|
407
|
+
terminal.log(`Sample newly added codes: ${sampleNewCodes.join(', ')}`);
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
410
|
|
|
@@ -529,7 +530,7 @@ class RxNormRelationshipExpander {
|
|
|
529
530
|
|
|
530
531
|
async expandCodes(initialCodes) {
|
|
531
532
|
if (this.verbose) {
|
|
532
|
-
|
|
533
|
+
terminal.log(` Starting relationship expansion with ${initialCodes.size} codes`);
|
|
533
534
|
}
|
|
534
535
|
|
|
535
536
|
let currentCodes = new Set(initialCodes);
|
|
@@ -540,7 +541,7 @@ class RxNormRelationshipExpander {
|
|
|
540
541
|
const sizeBefore = currentCodes.size;
|
|
541
542
|
|
|
542
543
|
if (this.verbose) {
|
|
543
|
-
|
|
544
|
+
terminal.log(` Iteration ${iteration}: Starting with ${sizeBefore} codes`);
|
|
544
545
|
}
|
|
545
546
|
|
|
546
547
|
const newCodes = await this.findRelatedCodes(currentCodes);
|
|
@@ -554,13 +555,13 @@ class RxNormRelationshipExpander {
|
|
|
554
555
|
const added = sizeAfter - sizeBefore;
|
|
555
556
|
|
|
556
557
|
if (this.verbose) {
|
|
557
|
-
|
|
558
|
+
terminal.log(` Iteration ${iteration}: Added ${added} codes (total: ${sizeAfter})`);
|
|
558
559
|
}
|
|
559
560
|
|
|
560
561
|
// Stop if no new codes were found
|
|
561
562
|
if (added === 0) {
|
|
562
563
|
if (this.verbose) {
|
|
563
|
-
|
|
564
|
+
terminal.log(` Expansion converged after ${iteration} iterations`);
|
|
564
565
|
}
|
|
565
566
|
break;
|
|
566
567
|
}
|
|
@@ -575,7 +576,7 @@ class RxNormRelationshipExpander {
|
|
|
575
576
|
|
|
576
577
|
if (!fs.existsSync(rxnrelPath)) {
|
|
577
578
|
if (this.verbose) {
|
|
578
|
-
|
|
579
|
+
terminal.log(` RXNREL file not found: ${rxnrelPath}`);
|
|
579
580
|
}
|
|
580
581
|
return relatedCodes;
|
|
581
582
|
}
|
|
@@ -624,12 +625,12 @@ class RxNormRelationshipExpander {
|
|
|
624
625
|
}
|
|
625
626
|
|
|
626
627
|
if (processedLines % 100000 === 0 && this.verbose) {
|
|
627
|
-
|
|
628
|
+
terminal.log(` Processed ${processedLines} relationships, found ${matchedRelationships} matches`);
|
|
628
629
|
}
|
|
629
630
|
}
|
|
630
631
|
|
|
631
632
|
if (this.verbose) {
|
|
632
|
-
|
|
633
|
+
terminal.log(` Found ${relatedCodes.size} related codes from ${matchedRelationships} relationships`);
|
|
633
634
|
}
|
|
634
635
|
|
|
635
636
|
return relatedCodes;
|
|
@@ -890,7 +891,7 @@ class RxNormSubsetProcessor {
|
|
|
890
891
|
|
|
891
892
|
if (this.verbose && lineNum > 0) {
|
|
892
893
|
const filename = path.basename(sourcePath);
|
|
893
|
-
|
|
894
|
+
terminal.log(` Included ${includedLines.toLocaleString()} of ${lineNum.toLocaleString()} lines in ${filename}`);
|
|
894
895
|
}
|
|
895
896
|
}
|
|
896
897
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the importers print: progress and results for the person running them (stdout), and
|
|
3
|
+
* problems (stderr). The importers are command line tools, so this is the console - but
|
|
4
|
+
* everything goes through here, so console calls stay out of the importer code (and out of a
|
|
5
|
+
* search for console.log).
|
|
6
|
+
*
|
|
7
|
+
* Each function looks console up when it is called, so whatever has replaced console.log (a
|
|
8
|
+
* test, say) still gets the output. Code that wants to see what an importer prints can replace
|
|
9
|
+
* terminal.log itself, and put it back after (see import-unii.module.js).
|
|
10
|
+
*/
|
|
11
|
+
/* eslint-disable no-console */
|
|
12
|
+
const terminal = {
|
|
13
|
+
log: (...args) => console.log(...args),
|
|
14
|
+
warn: (...args) => console.warn(...args),
|
|
15
|
+
error: (...args) => console.error(...args)
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = terminal;
|
|
@@ -4,6 +4,7 @@ const cliProgress = require('cli-progress');
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { getConfigManager } = require('./tx-import-settings');
|
|
7
|
+
const terminal = require('./terminal');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Base class for terminology import modules
|
|
@@ -167,14 +168,14 @@ class BaseTerminologyModule {
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
async confirmImport(config) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
terminal.log(chalk.cyan(`\nš ${this.getName()} Import Configuration:`));
|
|
172
|
+
terminal.log(` Source: ${chalk.white(config.source)}`);
|
|
173
|
+
terminal.log(` Destination: ${chalk.white(config.dest)}`);
|
|
174
|
+
terminal.log(` Version: ${chalk.white(config.version || 'Not specified')}`);
|
|
175
|
+
terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
|
|
175
176
|
|
|
176
177
|
if (config.estimatedDuration) {
|
|
177
|
-
|
|
178
|
+
terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
const { confirmed } = await inquirer.prompt({
|
|
@@ -218,19 +219,19 @@ class BaseTerminologyModule {
|
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
logInfo(message) {
|
|
221
|
-
|
|
222
|
+
terminal.log(chalk.blue('ā¹'), message);
|
|
222
223
|
}
|
|
223
224
|
|
|
224
225
|
logSuccess(message) {
|
|
225
|
-
|
|
226
|
+
terminal.log(chalk.green('ā'), message);
|
|
226
227
|
}
|
|
227
228
|
|
|
228
229
|
logWarning(message) {
|
|
229
|
-
|
|
230
|
+
terminal.log(chalk.yellow('ā '), message);
|
|
230
231
|
}
|
|
231
232
|
|
|
232
233
|
logError(message) {
|
|
233
|
-
|
|
234
|
+
terminal.log(chalk.red('ā'), message);
|
|
234
235
|
}
|
|
235
236
|
|
|
236
237
|
async validatePrerequisites(config) {
|
|
@@ -299,7 +300,7 @@ class BaseTerminologyModule {
|
|
|
299
300
|
// Common import workflow
|
|
300
301
|
async runImport(config) {
|
|
301
302
|
try {
|
|
302
|
-
|
|
303
|
+
terminal.log(chalk.blue.bold(`š„ Starting ${this.getName()} Import...\n`));
|
|
303
304
|
|
|
304
305
|
// Pre-flight checks
|
|
305
306
|
this.logInfo('Running pre-flight checks...');
|
|
@@ -321,7 +322,7 @@ class BaseTerminologyModule {
|
|
|
321
322
|
this.stopProgress(); // Ensure progress bar is cleaned up
|
|
322
323
|
this.logError(`${this.getName()} import failed: ${error.message}`);
|
|
323
324
|
if (config.verbose) {
|
|
324
|
-
|
|
325
|
+
terminal.error(error.stack);
|
|
325
326
|
}
|
|
326
327
|
process.exit(1);
|
|
327
328
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
|
+
const terminal = require('./terminal');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Manages persistent configuration for terminology import tools
|
|
@@ -31,7 +32,7 @@ class ConfigManager {
|
|
|
31
32
|
this.config = JSON.parse(fs.readFileSync(this.configFile, 'utf8'));
|
|
32
33
|
}
|
|
33
34
|
} catch (error) {
|
|
34
|
-
|
|
35
|
+
terminal.warn(`Warning: Could not load config: ${error.message}`);
|
|
35
36
|
this.config = {};
|
|
36
37
|
}
|
|
37
38
|
}
|
|
@@ -42,7 +43,7 @@ class ConfigManager {
|
|
|
42
43
|
this.history = JSON.parse(fs.readFileSync(this.historyFile, 'utf8'));
|
|
43
44
|
}
|
|
44
45
|
} catch (error) {
|
|
45
|
-
|
|
46
|
+
terminal.warn(`Warning: Could not load history: ${error.message}`);
|
|
46
47
|
this.history = {};
|
|
47
48
|
}
|
|
48
49
|
}
|
|
@@ -51,7 +52,7 @@ class ConfigManager {
|
|
|
51
52
|
try {
|
|
52
53
|
fs.writeFileSync(this.configFile, JSON.stringify(this.config, null, 2));
|
|
53
54
|
} catch (error) {
|
|
54
|
-
|
|
55
|
+
terminal.warn(`Warning: Could not save config: ${error.message}`);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -59,7 +60,7 @@ class ConfigManager {
|
|
|
59
60
|
try {
|
|
60
61
|
fs.writeFileSync(this.historyFile, JSON.stringify(this.history, null, 2));
|
|
61
62
|
} catch (error) {
|
|
62
|
-
|
|
63
|
+
terminal.warn(`Warning: Could not save history: ${error.message}`);
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|