fhirsmith 0.18.0 → 0.18.2
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/CHANGELOG.md +72 -10
- package/ips/ips.js +251 -26
- package/ips/jurisdictions.js +65 -0
- package/ips/pipeline.js +67 -6
- package/ips/profile-editor.js +283 -4
- package/ips/readme.md +48 -5
- package/ips/render.js +317 -46
- package/ips/scripts.js +46 -0
- package/ips/store.js +55 -0
- package/library/daily-report.js +25 -15
- package/package.json +3 -3
- package/registry/api.js +179 -1
- package/registry/crawler.js +2 -0
- package/registry/documentation/api.md +68 -2
- 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.js +53 -2
- package/translations/Messages.properties +1 -0
- package/tx/README.md +4 -0
- package/tx/closure/closure-store.js +14 -0
- package/tx/cm/cm-package.js +21 -15
- package/tx/cs/cs-api.js +3 -1
- package/tx/cs/cs-cs.js +59 -12
- package/tx/cs/cs-icf.js +47 -34
- package/tx/cs/cs-loinc.js +257 -3
- package/tx/cs/cs-snomed.js +544 -55
- package/tx/importers/csv-records.js +35 -0
- package/tx/importers/import-loinc.module.js +177 -41
- package/tx/importers/import-sct.module.js +131 -1
- package/tx/importers/subset-loinc.module.js +25 -6
- package/tx/library/codesystem.js +4 -1
- package/tx/library/designations.js +23 -30
- package/tx/library/tho-precedence.js +40 -0
- package/tx/openapi-operations.js +2 -1
- package/tx/provider.js +46 -29
- package/tx/sct/documentation.md +45 -21
- package/tx/sct/ecl.js +34 -11
- package/tx/sct/structures.js +15 -2
- package/tx/tx.fhir.org.yml +2 -2
- package/tx/vs/vs-package.js +17 -13
- package/tx/vs/vs-vsac.js +1 -1
- package/tx/workers/closure.js +20 -0
- package/tx/workers/expand.js +2 -2
- package/tx/workers/validate.js +10 -0
- package/utilities/make-sct-loinc-subset.js +250 -0
package/tx/workers/closure.js
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
// already exists, unless reset = true, which wipes it
|
|
18
18
|
// name + concept(s) add concepts; returns the entries they produce
|
|
19
19
|
// name + version resync; returns every entry added after version
|
|
20
|
+
// name + dispose = true the client has finished with the table, so it is
|
|
21
|
+
// deleted. Not in the specification: the client is
|
|
22
|
+
// just telling the server it can let the table go.
|
|
23
|
+
// Disposing of a table that doesn't exist is fine
|
|
24
|
+
// (it may already have been pruned). It can't be
|
|
25
|
+
// combined with anything else
|
|
20
26
|
//
|
|
21
27
|
// The configuration is fixed when the table is initialised, and replayed on every later
|
|
22
28
|
// call: tx-resource (the code systems it is built over, which the client does not send
|
|
@@ -147,6 +153,7 @@ class ClosureWorker extends TerminologyWorker {
|
|
|
147
153
|
const config = [];
|
|
148
154
|
let versionParam = null;
|
|
149
155
|
let reset = false;
|
|
156
|
+
let dispose = false;
|
|
150
157
|
const ignored = [];
|
|
151
158
|
for (const p of list) {
|
|
152
159
|
if (p.name === 'concept') {
|
|
@@ -157,6 +164,8 @@ class ClosureWorker extends TerminologyWorker {
|
|
|
157
164
|
versionParam = p;
|
|
158
165
|
} else if (p.name === 'reset') {
|
|
159
166
|
reset = p.valueBoolean === true || p.valueString === 'true';
|
|
167
|
+
} else if (p.name === 'dispose') {
|
|
168
|
+
dispose = p.valueBoolean === true || p.valueString === 'true';
|
|
160
169
|
} else if (p.name !== 'name') {
|
|
161
170
|
// Ignored, as everywhere else in the server: a request carries parameters that
|
|
162
171
|
// are not for the operation in hand. The tx-ecosystem runner, for one, adds a
|
|
@@ -167,6 +176,17 @@ class ClosureWorker extends TerminologyWorker {
|
|
|
167
176
|
if (ignored.length > 0) {
|
|
168
177
|
this.log.debug(`$closure: ignored parameter(s) ${ignored.map(safeText).join(', ')}`);
|
|
169
178
|
}
|
|
179
|
+
if (dispose) {
|
|
180
|
+
if (concepts.length > 0 || versionParam || reset || config.length > 0) {
|
|
181
|
+
throw this.closureIssue('invalid', 'CLOSURE_DISPOSE_NOT_ALONE', [], 'invalid-data', 400);
|
|
182
|
+
}
|
|
183
|
+
return await this.lockTable(name, async () => {
|
|
184
|
+
if (this.store.dropTable(name)) {
|
|
185
|
+
this.log.info(`closure table '${name}' disposed`);
|
|
186
|
+
}
|
|
187
|
+
return await this.buildConceptMap(name, 0, [], fhirVersion);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
170
190
|
if (concepts.length > 0 && versionParam) {
|
|
171
191
|
throw this.closureIssue('invalid', 'CLOSURE_CONCEPT_AND_VERSION', [], 'invalid-data', 400);
|
|
172
192
|
}
|
package/tx/workers/expand.js
CHANGED
|
@@ -439,8 +439,8 @@ class ValueSetExpander {
|
|
|
439
439
|
if (!n.designation) {
|
|
440
440
|
n.designation = [];
|
|
441
441
|
}
|
|
442
|
-
if (t.
|
|
443
|
-
this.reportedSupplements.add(t.
|
|
442
|
+
if (t.supplement && t.supplement.vurl) {
|
|
443
|
+
this.reportedSupplements.add(t.supplement.vurl);
|
|
444
444
|
}
|
|
445
445
|
n.designation.push(t.asObject());
|
|
446
446
|
}
|
package/tx/workers/validate.js
CHANGED
|
@@ -2467,6 +2467,16 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2467
2467
|
|
|
2468
2468
|
let vs = this.makeVsForCS(codeSystem);
|
|
2469
2469
|
|
|
2470
|
+
// The checker isn't prepared (there is no real value set to analyse), and preparing is
|
|
2471
|
+
// where the supplements asked for with useSupplement are found. So note the ones the code
|
|
2472
|
+
// system has here, or every useSupplement on CodeSystem/$validate-code is reported as
|
|
2473
|
+
// missing, even when the supplement was passed in tx-resource
|
|
2474
|
+
for (const s of this.requiredSupplements) {
|
|
2475
|
+
if (codeSystem.hasSupplement(s)) {
|
|
2476
|
+
this.usedSupplements.add(s);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2470
2480
|
// Create and prepare checker
|
|
2471
2481
|
const checker = new ValueSetChecker(this, vs, params);
|
|
2472
2482
|
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generates the LOINC Extension (LOINC ontology, module 11010000107,
|
|
4
|
+
* namespace 1010000) content for the SNOMED CT test edition that the
|
|
5
|
+
* tx-ecosystem tests use.
|
|
6
|
+
*
|
|
7
|
+
* The agreed scope for LOINC Extension support is:
|
|
8
|
+
* - $lookup on a SNOMED concept returns its LOINC code
|
|
9
|
+
* - $translate using the implicit map ?fhir_cm=11010000107 (the LOINC module)
|
|
10
|
+
* - $expand with a filter for concepts that have a LOINC code
|
|
11
|
+
*
|
|
12
|
+
* None of that needs the LOINC ontology's concept model, so this script takes
|
|
13
|
+
* a handful of real LOINC Extension concepts from the published release and
|
|
14
|
+
* copies their concept, description, language refset and identifier rows,
|
|
15
|
+
* but NOT their defining relationships or OWL axioms (those reference
|
|
16
|
+
* hundreds of concepts that are not in the test subset). Instead each concept
|
|
17
|
+
* gets a single generated IS A to an observable entity that is in the
|
|
18
|
+
* subset, and is made primitive (a fully defined concept defined only by an
|
|
19
|
+
* IS A would be equivalent to its parent).
|
|
20
|
+
*
|
|
21
|
+
* The two metadata concepts the identifiers hang off - the module concept and
|
|
22
|
+
* the LOINC identifier scheme - are copied too, re-parented to metadata
|
|
23
|
+
* concepts that are in the subset (their real parents are not).
|
|
24
|
+
*
|
|
25
|
+
* All rows are given the edition's effective time (20250909). Everything else
|
|
26
|
+
* - ids, terms, case significance, acceptability, LOINC codes - is as
|
|
27
|
+
* published. Rows are deterministic, so the script can be re-run safely.
|
|
28
|
+
*
|
|
29
|
+
* Usage:
|
|
30
|
+
* node utilities/make-sct-loinc-subset.js <LOINC Extension release folder> [tx/data/snomed]
|
|
31
|
+
* e.g.
|
|
32
|
+
* node utilities/make-sct-loinc-subset.js \
|
|
33
|
+
* ~/archive/terminologies/SnomedCT_LOINCExtension_PRODUCTION_LO1010000_20261021T120000Z \
|
|
34
|
+
* ../../igs/tx-ecosystem-ig/tx-source/snomed
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
const fs = require('fs');
|
|
38
|
+
const path = require('path');
|
|
39
|
+
const crypto = require('crypto');
|
|
40
|
+
|
|
41
|
+
const source = process.argv[2];
|
|
42
|
+
if (!source) {
|
|
43
|
+
console.error('Usage: node utilities/make-sct-loinc-subset.js <LOINC Extension release folder> [target snomed folder]');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
const root = process.argv[3] || path.join(__dirname, '..', 'tx', 'data', 'snomed');
|
|
47
|
+
const TAG = 'LO1010000';
|
|
48
|
+
const ET = '20250909';
|
|
49
|
+
const MODULE = '11010000107'; // LOINC Extension module
|
|
50
|
+
const EDITION_MODULE = '31000003106'; // the test edition module
|
|
51
|
+
const NAMESPACE = '1010000';
|
|
52
|
+
const EOL = '\r\n';
|
|
53
|
+
|
|
54
|
+
// --- RF2 constants ------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
const PRIMITIVE = '900000000000074008';
|
|
57
|
+
const IS_A = '116680003';
|
|
58
|
+
const INFERRED = '900000000000011006';
|
|
59
|
+
const EXISTENTIAL = '900000000000451002';
|
|
60
|
+
const OWL_AXIOM_REFSET = '733073007';
|
|
61
|
+
const MODULE_DEPENDENCY_REFSET = '900000000000534007';
|
|
62
|
+
const INT_CORE = '900000000000207008';
|
|
63
|
+
const INT_MODEL = '900000000000012004';
|
|
64
|
+
const INT_ET = '20250801'; // the International release the subset was extracted from
|
|
65
|
+
|
|
66
|
+
// --- The subset ---------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
// LOINC code -> parent in the test subset (null = inactive, no relationships).
|
|
69
|
+
// The LOINC concept id is taken from the release's identifier file.
|
|
70
|
+
const loincCodes = [
|
|
71
|
+
// all but the last are in the tx-ecosystem LOINC test subset, so they can be translated both ways
|
|
72
|
+
['2823-3', '11953005'], // Potassium [Moles/volume] in Serum or Plasma -> Renal function
|
|
73
|
+
['22760-3', '11953005'], // Potassium [Mass/volume] in Serum or Plasma -> Renal function
|
|
74
|
+
['6298-4', '11953005'], // Potassium [Moles/volume] in Blood -> Renal function
|
|
75
|
+
['2828-2', '11953005'], // Potassium [Moles/volume] in Urine -> Renal function
|
|
76
|
+
['14402-2', '11953005'], // Creatinine [Mass/volume] in Gastric fluid -> Renal function
|
|
77
|
+
['14394-1', '11953005'], // Urea nitrogen [Mass/volume] in Pleural fluid -> Renal function
|
|
78
|
+
['104597-0', '363787002'], // Glucose [Mass/volume] in Venous blood by Glucometer -> Observable entity
|
|
79
|
+
['95084-0', '363787002'], // C peptide [Mass/volume] in Serum or Plasma --2 hours post meal -> Observable entity
|
|
80
|
+
['95095-6', '363787002'], // Glucagon [Mass/volume] in Serum or Plasma --2 hours post meal -> Observable entity
|
|
81
|
+
['31157-1', '363787002'], // Carboxyhemoglobin [Mass/volume] in Blood -> Observable entity
|
|
82
|
+
['95594-8', '363787002'], // Calcifediol [Mass/volume] in Capillary blood -> Observable entity
|
|
83
|
+
['2067-7', null] // inactive in the release, but still has an (active) LOINC identifier; not in the LOINC test subset
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
// metadata concepts -> substitute parent in the test subset
|
|
87
|
+
const metadata = [
|
|
88
|
+
[MODULE, '900000000000443000'], // LOINC Extension module -> Module (real parent 1201891009 not in subset)
|
|
89
|
+
['30051010000102', '900000000000454005'] // LOINC code identifier -> Foundation metadata concept (real parent 900000000000453004 not in subset)
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
// --- Read the release ---------------------------------------------------
|
|
93
|
+
|
|
94
|
+
function findFile(dir, prefix) {
|
|
95
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
96
|
+
const p = path.join(dir, entry.name);
|
|
97
|
+
if (entry.isDirectory()) {
|
|
98
|
+
const r = findFile(p, prefix);
|
|
99
|
+
if (r) return r;
|
|
100
|
+
} else if (entry.name.startsWith(prefix)) {
|
|
101
|
+
return p;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readRf2(prefix) {
|
|
108
|
+
const file = findFile(path.join(source, 'Snapshot'), prefix);
|
|
109
|
+
if (!file) throw new Error(`No ${prefix}* file in ${source}/Snapshot`);
|
|
110
|
+
const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/).filter(l => l.length > 0);
|
|
111
|
+
return { header: lines[0], rows: lines.slice(1).map(l => l.split('\t')) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const identifiers = readRf2('sct2_Identifier_Snapshot');
|
|
115
|
+
const conceptFile = readRf2('sct2_Concept_Snapshot');
|
|
116
|
+
const descriptionFile = readRf2('sct2_Description_Snapshot');
|
|
117
|
+
const languageFile = readRf2('der2_cRefset_LanguageSnapshot');
|
|
118
|
+
const relationshipFile = readRf2('sct2_Relationship_Snapshot');
|
|
119
|
+
|
|
120
|
+
const conceptById = new Map(conceptFile.rows.map(r => [r[0], r]));
|
|
121
|
+
const identifierByCode = new Map(identifiers.rows.map(r => [r[0], r]));
|
|
122
|
+
|
|
123
|
+
// --- Ids for generated rows ---------------------------------------------
|
|
124
|
+
|
|
125
|
+
const d = [
|
|
126
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 0, 6, 7, 8, 9, 5], [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
|
|
127
|
+
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7], [4, 0, 1, 2, 3, 9, 5, 6, 7, 8], [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
|
|
128
|
+
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2], [7, 6, 5, 9, 8, 2, 1, 0, 4, 3], [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
|
|
129
|
+
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]];
|
|
130
|
+
const p = [
|
|
131
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
|
|
132
|
+
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7], [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
|
|
133
|
+
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5], [7, 0, 4, 6, 8, 1, 5, 3, 9, 2]];
|
|
134
|
+
const inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];
|
|
135
|
+
|
|
136
|
+
function verhoeff(numStr) {
|
|
137
|
+
let c = 0;
|
|
138
|
+
const digits = numStr.split('').reverse().map(Number);
|
|
139
|
+
for (let i = 0; i < digits.length; i++) {
|
|
140
|
+
c = d[c][p[(i + 1) % 8][digits[i]]];
|
|
141
|
+
}
|
|
142
|
+
return inv[c];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Relationship ids are allocated from a high item range and checked against
|
|
146
|
+
// the release so they can never collide with a published relationship id.
|
|
147
|
+
const usedRelationshipIds = new Set(relationshipFile.rows.map(r => r[0]));
|
|
148
|
+
let nextItem = 99000001;
|
|
149
|
+
function relationshipId() {
|
|
150
|
+
for (;;) {
|
|
151
|
+
const body = `${nextItem++}${NAMESPACE}12`;
|
|
152
|
+
const id = body + verhoeff(body);
|
|
153
|
+
if (!usedRelationshipIds.has(id)) return id;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function memberId(...parts) {
|
|
158
|
+
const h = crypto.createHash('sha1').update(['sct-loinc-subset', ...parts].join('|')).digest('hex');
|
|
159
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-5${h.slice(13, 16)}-a${h.slice(17, 20)}-${h.slice(20, 32)}`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// --- Build rows ---------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
const wanted = []; // [conceptId, parent|null, loincCode|null]
|
|
165
|
+
for (const [code, parent] of loincCodes) {
|
|
166
|
+
const ident = identifierByCode.get(code);
|
|
167
|
+
if (!ident) throw new Error(`LOINC code ${code} has no identifier in the release`);
|
|
168
|
+
if (ident[3] !== MODULE) throw new Error(`Identifier for ${code} is in module ${ident[3]}, not ${MODULE}`);
|
|
169
|
+
wanted.push([ident[5], parent, code]);
|
|
170
|
+
}
|
|
171
|
+
for (const [id, parent] of metadata) wanted.push([id, parent, null]);
|
|
172
|
+
|
|
173
|
+
const conceptRows = [];
|
|
174
|
+
const relationshipRows = [];
|
|
175
|
+
const owlRows = [];
|
|
176
|
+
const identifierRows = [];
|
|
177
|
+
const summary = [];
|
|
178
|
+
const wantedIds = new Set(wanted.map(w => w[0]));
|
|
179
|
+
|
|
180
|
+
for (const [id, parent, code] of wanted) {
|
|
181
|
+
const c = conceptById.get(id);
|
|
182
|
+
if (!c) throw new Error(`Concept ${id} not found in the release`);
|
|
183
|
+
if (c[3] !== MODULE) throw new Error(`Concept ${id} is in module ${c[3]}, not ${MODULE}`);
|
|
184
|
+
const active = c[2];
|
|
185
|
+
if (active === '1' && !parent) throw new Error(`Active concept ${id} needs a parent`);
|
|
186
|
+
if (active === '0' && parent) throw new Error(`Inactive concept ${id} must not be given a parent`);
|
|
187
|
+
conceptRows.push([id, ET, active, MODULE, PRIMITIVE]);
|
|
188
|
+
if (parent) {
|
|
189
|
+
relationshipRows.push([relationshipId(), ET, '1', MODULE, id, parent, '0', IS_A, INFERRED, EXISTENTIAL]);
|
|
190
|
+
owlRows.push([memberId('owl', id), ET, '1', MODULE, OWL_AXIOM_REFSET, id, `SubClassOf(:${id} :${parent})`]);
|
|
191
|
+
}
|
|
192
|
+
if (code) {
|
|
193
|
+
const ident = identifierByCode.get(code);
|
|
194
|
+
identifierRows.push([ident[0], ET, ident[2], ident[3], ident[4], ident[5]]);
|
|
195
|
+
}
|
|
196
|
+
summary.push({ id, code, active, parent });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const descriptionRows = descriptionFile.rows
|
|
200
|
+
.filter(r => wantedIds.has(r[4]))
|
|
201
|
+
.map(r => [r[0], ET, ...r.slice(2)]);
|
|
202
|
+
const descriptionIds = new Set(descriptionRows.map(r => r[0]));
|
|
203
|
+
for (const s of summary) {
|
|
204
|
+
const fsn = descriptionRows.find(r => r[4] === s.id && r[2] === '1' && r[6] === '900000000000003001');
|
|
205
|
+
if (!fsn) throw new Error(`No active FSN for ${s.id}`);
|
|
206
|
+
s.fsn = fsn[7];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const languageRows = languageFile.rows
|
|
210
|
+
.filter(r => descriptionIds.has(r[5]))
|
|
211
|
+
.map(r => [r[0], ET, ...r.slice(2)]);
|
|
212
|
+
|
|
213
|
+
const moduleDependencyRows = [
|
|
214
|
+
[memberId('dep', MODULE, INT_CORE), ET, '1', MODULE, MODULE_DEPENDENCY_REFSET, INT_CORE, ET, INT_ET],
|
|
215
|
+
[memberId('dep', MODULE, INT_MODEL), ET, '1', MODULE, MODULE_DEPENDENCY_REFSET, INT_MODEL, ET, INT_ET],
|
|
216
|
+
[memberId('dep', EDITION_MODULE, MODULE), ET, '1', EDITION_MODULE, MODULE_DEPENDENCY_REFSET, MODULE, ET, ET]
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
// --- Write files --------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
function sortRows(rows) {
|
|
222
|
+
return rows.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function write(rel, header, rows) {
|
|
226
|
+
const file = path.join(root, rel);
|
|
227
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
228
|
+
fs.writeFileSync(file, [header, ...sortRows(rows).map(r => r.join('\t'))].join(EOL) + EOL, 'utf8');
|
|
229
|
+
console.log(`${rows.length.toString().padStart(3)} rows ${rel}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
write(`Terminology/sct2_Concept_Snapshot_${TAG}_${ET}.txt`,
|
|
233
|
+
'id\teffectiveTime\tactive\tmoduleId\tdefinitionStatusId', conceptRows);
|
|
234
|
+
write(`Terminology/sct2_Description_Snapshot-en_${TAG}_${ET}.txt`,
|
|
235
|
+
'id\teffectiveTime\tactive\tmoduleId\tconceptId\tlanguageCode\ttypeId\tterm\tcaseSignificanceId', descriptionRows);
|
|
236
|
+
write(`Terminology/sct2_Relationship_Snapshot_${TAG}_${ET}.txt`,
|
|
237
|
+
'id\teffectiveTime\tactive\tmoduleId\tsourceId\tdestinationId\trelationshipGroup\ttypeId\tcharacteristicTypeId\tmodifierId', relationshipRows);
|
|
238
|
+
write(`Terminology/sct2_sRefset_OWLExpressionSnapshot_${TAG}_${ET}.txt`,
|
|
239
|
+
'id\teffectiveTime\tactive\tmoduleId\trefsetId\treferencedComponentId\towlExpression', owlRows);
|
|
240
|
+
write(`Terminology/sct2_Identifier_Snapshot_${TAG}_${ET}.txt`,
|
|
241
|
+
'alternateIdentifier\teffectiveTime\tactive\tmoduleId\tidentifierSchemeId\treferencedComponentId', identifierRows);
|
|
242
|
+
write(`Refset/Language/der2_cRefset_LanguageSnapshot-en_${TAG}_${ET}.txt`,
|
|
243
|
+
'id\teffectiveTime\tactive\tmoduleId\trefsetId\treferencedComponentId\tacceptabilityId', languageRows);
|
|
244
|
+
write(`Refset/Metadata/der2_ssRefset_ModuleDependencySnapshot_${TAG}_${ET}.txt`,
|
|
245
|
+
'id\teffectiveTime\tactive\tmoduleId\trefsetId\treferencedComponentId\tsourceEffectiveTime\ttargetEffectiveTime', moduleDependencyRows);
|
|
246
|
+
|
|
247
|
+
console.log('\nConcepts:');
|
|
248
|
+
for (const s of summary) {
|
|
249
|
+
console.log(` ${s.id.padEnd(16)} ${(s.code || '').padEnd(8)} ${s.active === '0' ? '(inactive) ' : ''}${s.fsn}`);
|
|
250
|
+
}
|