fhirsmith 0.8.6 → 0.9.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/CHANGELOG.md +27 -0
- package/library/regex-utilities.js +13 -0
- package/package.json +3 -2
- package/publisher/publisher.js +28 -7
- package/root-bare-template.html +9759 -37
- package/translations/Messages.properties +1 -0
- package/tx/cs/cs-country.js +2 -1
- package/tx/cs/cs-cs.js +4 -3
- package/tx/cs/cs-loinc.js +2 -1
- package/tx/cs/cs-snomed.js +1 -1
- package/tx/data/OperationDefinition-ValueSet-related.json +133 -0
- package/tx/importers/atc-to-fhir.js +27 -27
- package/tx/library/ucum-parsers.js +2 -1
- package/tx/ocl/cs-ocl.cjs +48 -15
- package/tx/ocl/vs-ocl.cjs +57 -34
- package/tx/tx.fhir.org.yml +4 -4
- package/tx/vs/vs-database.js +150 -100
- package/tx/vs/vs-vsac.js +90 -31
- package/tx/workers/expand.js +5 -1
- package/tx/workers/metadata.js +3 -1
package/tx/vs/vs-database.js
CHANGED
|
@@ -37,9 +37,9 @@ class ValueSetDatabase {
|
|
|
37
37
|
if (!hasCol) {
|
|
38
38
|
migrations.push(new Promise((res, rej) => {
|
|
39
39
|
db.run(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
"ALTER TABLE valuesets ADD COLUMN date_first_seen INTEGER DEFAULT 0",
|
|
41
|
+
[],
|
|
42
|
+
(err) => err ? rej(err) : res()
|
|
43
43
|
);
|
|
44
44
|
}));
|
|
45
45
|
}
|
|
@@ -47,13 +47,22 @@ class ValueSetDatabase {
|
|
|
47
47
|
migrations.push(new Promise((res, rej) => {
|
|
48
48
|
db.run(`
|
|
49
49
|
CREATE TABLE IF NOT EXISTS vsac_runs (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
51
|
+
started_at INTEGER NOT NULL,
|
|
52
|
+
finished_at INTEGER,
|
|
53
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
54
|
+
error_message TEXT,
|
|
55
|
+
total_fetched INTEGER,
|
|
56
|
+
total_new INTEGER
|
|
57
|
+
)
|
|
58
|
+
`, [], (err) => err ? rej(err) : res());
|
|
59
|
+
}));
|
|
60
|
+
// Ensure vsac_settings table exists (for _lastUpdated tracking etc.)
|
|
61
|
+
migrations.push(new Promise((res, rej) => {
|
|
62
|
+
db.run(`
|
|
63
|
+
CREATE TABLE IF NOT EXISTS vsac_settings (
|
|
64
|
+
key TEXT PRIMARY KEY,
|
|
65
|
+
value TEXT
|
|
57
66
|
)
|
|
58
67
|
`, [], (err) => err ? rej(err) : res());
|
|
59
68
|
}));
|
|
@@ -170,69 +179,77 @@ class ValueSetDatabase {
|
|
|
170
179
|
db.serialize(() => {
|
|
171
180
|
// Main value sets table
|
|
172
181
|
db.run(`
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
CREATE TABLE valuesets (
|
|
183
|
+
id TEXT PRIMARY KEY,
|
|
184
|
+
url TEXT,
|
|
185
|
+
version TEXT,
|
|
186
|
+
date TEXT,
|
|
187
|
+
description TEXT,
|
|
188
|
+
effectivePeriod_start TEXT,
|
|
189
|
+
effectivePeriod_end TEXT,
|
|
190
|
+
expansion_identifier TEXT,
|
|
191
|
+
name TEXT,
|
|
192
|
+
publisher TEXT,
|
|
193
|
+
status TEXT,
|
|
194
|
+
title TEXT,
|
|
195
|
+
content TEXT NOT NULL,
|
|
196
|
+
last_seen INTEGER DEFAULT (strftime('%s', 'now')),
|
|
197
|
+
date_first_seen INTEGER DEFAULT (strftime('%s', 'now'))
|
|
198
|
+
)
|
|
190
199
|
`);
|
|
191
200
|
|
|
192
201
|
// Identifiers table (0..* Identifier)
|
|
193
202
|
db.run(`
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
CREATE TABLE valueset_identifiers (
|
|
204
|
+
valueset_id TEXT,
|
|
205
|
+
system TEXT,
|
|
206
|
+
value TEXT,
|
|
207
|
+
use_code TEXT,
|
|
208
|
+
type_system TEXT,
|
|
209
|
+
type_code TEXT,
|
|
210
|
+
FOREIGN KEY (valueset_id) REFERENCES valuesets(url)
|
|
211
|
+
)
|
|
203
212
|
`);
|
|
204
213
|
|
|
205
214
|
// Jurisdictions table (0..* CodeableConcept with 0..* Coding)
|
|
206
215
|
db.run(`
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
216
|
+
CREATE TABLE valueset_jurisdictions (
|
|
217
|
+
valueset_id TEXT,
|
|
218
|
+
system TEXT,
|
|
219
|
+
code TEXT,
|
|
220
|
+
display TEXT,
|
|
221
|
+
FOREIGN KEY (valueset_id) REFERENCES valuesets(url)
|
|
222
|
+
)
|
|
214
223
|
`);
|
|
215
224
|
|
|
216
225
|
// Systems table (from compose.include[].system)
|
|
217
226
|
db.run(`
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
227
|
+
CREATE TABLE valueset_systems (
|
|
228
|
+
valueset_id TEXT,
|
|
229
|
+
system TEXT,
|
|
230
|
+
version TEXT,
|
|
231
|
+
FOREIGN KEY (valueset_id) REFERENCES valuesets(url)
|
|
232
|
+
)
|
|
224
233
|
`);
|
|
225
234
|
|
|
226
235
|
// Run tracking table
|
|
227
236
|
db.run(`
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
237
|
+
CREATE TABLE vsac_runs (
|
|
238
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
239
|
+
started_at INTEGER NOT NULL,
|
|
240
|
+
finished_at INTEGER,
|
|
241
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
242
|
+
error_message TEXT,
|
|
243
|
+
total_fetched INTEGER,
|
|
244
|
+
total_new INTEGER
|
|
245
|
+
)
|
|
246
|
+
`);
|
|
247
|
+
|
|
248
|
+
// Settings table (key-value store for _lastUpdated tracking etc.)
|
|
249
|
+
db.run(`
|
|
250
|
+
CREATE TABLE IF NOT EXISTS vsac_settings (
|
|
251
|
+
key TEXT PRIMARY KEY,
|
|
252
|
+
value TEXT
|
|
236
253
|
)
|
|
237
254
|
`);
|
|
238
255
|
|
|
@@ -271,9 +288,9 @@ class ValueSetDatabase {
|
|
|
271
288
|
const db = await this._getWriteConnection();
|
|
272
289
|
return new Promise((resolve, reject) => {
|
|
273
290
|
db.run(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
291
|
+
`INSERT INTO vsac_runs (started_at, status) VALUES (strftime('%s','now'), 'running')`,
|
|
292
|
+
[],
|
|
293
|
+
function(err) { err ? reject(err) : resolve(this.lastID); }
|
|
277
294
|
);
|
|
278
295
|
});
|
|
279
296
|
}
|
|
@@ -289,10 +306,10 @@ class ValueSetDatabase {
|
|
|
289
306
|
const db = await this._getWriteConnection();
|
|
290
307
|
return new Promise((resolve, reject) => {
|
|
291
308
|
db.run(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
309
|
+
`UPDATE vsac_runs SET finished_at = strftime('%s','now'), status = 'ok',
|
|
310
|
+
total_fetched = ?, total_new = ? WHERE id = ?`,
|
|
311
|
+
[totalFetched, totalNew, id],
|
|
312
|
+
err => err ? reject(err) : resolve()
|
|
296
313
|
);
|
|
297
314
|
});
|
|
298
315
|
}
|
|
@@ -307,10 +324,43 @@ class ValueSetDatabase {
|
|
|
307
324
|
const db = await this._getWriteConnection();
|
|
308
325
|
return new Promise((resolve, reject) => {
|
|
309
326
|
db.run(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
327
|
+
`UPDATE vsac_runs SET finished_at = strftime('%s','now'), status = 'error',
|
|
328
|
+
error_message = ? WHERE id = ?`,
|
|
329
|
+
[errorMessage, id],
|
|
330
|
+
err => err ? reject(err) : resolve()
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get a setting value from the vsac_settings table
|
|
337
|
+
* @param {string} key - The setting key
|
|
338
|
+
* @returns {Promise<string|null>} The setting value, or null if not found
|
|
339
|
+
*/
|
|
340
|
+
async getSetting(key) {
|
|
341
|
+
const db = await this._getReadConnection();
|
|
342
|
+
return new Promise((resolve, reject) => {
|
|
343
|
+
db.get('SELECT value FROM vsac_settings WHERE key = ?', [key], (err, row) => {
|
|
344
|
+
if (err) reject(err);
|
|
345
|
+
else resolve(row ? row.value : null);
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Set a setting value in the vsac_settings table
|
|
352
|
+
* @param {string} key - The setting key
|
|
353
|
+
* @param {string} value - The setting value
|
|
354
|
+
* @returns {Promise<void>}
|
|
355
|
+
*/
|
|
356
|
+
async setSetting(key, value) {
|
|
357
|
+
const db = await this._getWriteConnection();
|
|
358
|
+
return new Promise((resolve, reject) => {
|
|
359
|
+
db.run(
|
|
360
|
+
`INSERT INTO vsac_settings (key, value) VALUES (?, ?)
|
|
361
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
|
|
362
|
+
[key, value],
|
|
363
|
+
err => err ? reject(err) : resolve()
|
|
314
364
|
);
|
|
315
365
|
});
|
|
316
366
|
}
|
|
@@ -353,24 +403,24 @@ class ValueSetDatabase {
|
|
|
353
403
|
const expansionId = valueSet.expansion?.identifier || null;
|
|
354
404
|
|
|
355
405
|
db.run(`
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
406
|
+
INSERT INTO valuesets (
|
|
407
|
+
id, url, version, date, description, effectivePeriod_start, effectivePeriod_end,
|
|
408
|
+
expansion_identifier, name, publisher, status, title, content, last_seen, date_first_seen
|
|
409
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now'))
|
|
410
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
411
|
+
url=excluded.url,
|
|
412
|
+
version=excluded.version,
|
|
413
|
+
date=excluded.date,
|
|
414
|
+
description=excluded.description,
|
|
415
|
+
effectivePeriod_start=excluded.effectivePeriod_start,
|
|
416
|
+
effectivePeriod_end=excluded.effectivePeriod_end,
|
|
417
|
+
expansion_identifier=excluded.expansion_identifier,
|
|
418
|
+
name=excluded.name,
|
|
419
|
+
publisher=excluded.publisher,
|
|
420
|
+
status=excluded.status,
|
|
421
|
+
title=excluded.title,
|
|
422
|
+
content=excluded.content,
|
|
423
|
+
last_seen=strftime('%s', 'now')
|
|
374
424
|
`, [
|
|
375
425
|
valueSet.id,
|
|
376
426
|
valueSet.url,
|
|
@@ -414,10 +464,10 @@ class ValueSetDatabase {
|
|
|
414
464
|
|
|
415
465
|
return new Promise((resolve, reject) => {
|
|
416
466
|
db.run(`
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
467
|
+
update valuesets
|
|
468
|
+
set last_seen = strftime('%s', 'now')
|
|
469
|
+
where url = ?
|
|
470
|
+
and version = ?
|
|
421
471
|
`, [
|
|
422
472
|
valueSet.url,
|
|
423
473
|
valueSet.version
|
|
@@ -466,9 +516,9 @@ class ValueSetDatabase {
|
|
|
466
516
|
const typeCode = id.type?.coding?.[0]?.code || null;
|
|
467
517
|
|
|
468
518
|
db.run(`
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
519
|
+
INSERT INTO valueset_identifiers (
|
|
520
|
+
valueset_id, system, value, use_code, type_system, type_code
|
|
521
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
472
522
|
`, [
|
|
473
523
|
valueSet.id,
|
|
474
524
|
id.system || null,
|
|
@@ -490,9 +540,9 @@ class ValueSetDatabase {
|
|
|
490
540
|
for (const coding of jurisdiction.coding) {
|
|
491
541
|
pendingOperations++;
|
|
492
542
|
db.run(`
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
543
|
+
INSERT INTO valueset_jurisdictions (
|
|
544
|
+
valueset_id, system, code, display
|
|
545
|
+
) VALUES (?, ?, ?, ?)
|
|
496
546
|
`, [
|
|
497
547
|
valueSet.id,
|
|
498
548
|
coding.system || null,
|
|
@@ -514,7 +564,7 @@ class ValueSetDatabase {
|
|
|
514
564
|
pendingOperations++;
|
|
515
565
|
|
|
516
566
|
db.run(`
|
|
517
|
-
|
|
567
|
+
INSERT INTO valueset_systems (valueset_id, system, version) VALUES (?, ?, ?)
|
|
518
568
|
`, [valueSet.id, include.system, include.version], function(err) {
|
|
519
569
|
if (err) {
|
|
520
570
|
operationError(new Error(`Failed to insert system: ${err.message}`));
|
|
@@ -598,12 +648,12 @@ class ValueSetDatabase {
|
|
|
598
648
|
async search(spaceId, map, searchParams, elements = null) {
|
|
599
649
|
// Check if we can optimize by selecting only indexed columns
|
|
600
650
|
const canOptimize = elements && elements.length > 0 &&
|
|
601
|
-
|
|
651
|
+
elements.every(e => INDEXED_COLUMNS.includes(e));
|
|
602
652
|
|
|
603
653
|
// Always include 'id' in the columns to select when optimizing
|
|
604
654
|
const columnsToSelect = canOptimize
|
|
605
|
-
|
|
606
|
-
|
|
655
|
+
? (elements.includes('id') ? elements : ['id', ...elements])
|
|
656
|
+
: null;
|
|
607
657
|
|
|
608
658
|
const db = await this._getReadConnection();
|
|
609
659
|
|
package/tx/vs/vs-vsac.js
CHANGED
|
@@ -78,7 +78,6 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
|
|
|
78
78
|
if (this.valueSetMap.size == 0) {
|
|
79
79
|
await this.refreshValueSets();
|
|
80
80
|
}
|
|
81
|
-
|
|
82
81
|
// Start periodic refresh
|
|
83
82
|
this._startRefreshTimer();
|
|
84
83
|
this.initialized = true;
|
|
@@ -174,6 +173,14 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
|
|
|
174
173
|
console.log(`VSAC refresh phase 1 done. Total: ${count} with ${ncount} new items`);
|
|
175
174
|
this.stats.task('VSAC Sync', `VSAC refresh phase 1 done. Total: ${count} with ${ncount} new items`);
|
|
176
175
|
|
|
176
|
+
// phase 1b: query for recently updated value sets via _lastUpdated
|
|
177
|
+
let lastUpdatedCount = await this._scanLastUpdated();
|
|
178
|
+
console.log(`VSAC refresh phase 1b done. ${lastUpdatedCount} additional items from _lastUpdated`);
|
|
179
|
+
this.stats.task('VSAC Sync', `Phase 1b: ${lastUpdatedCount} from _lastUpdated`);
|
|
180
|
+
|
|
181
|
+
// deduplicate the queue
|
|
182
|
+
this.queue = [...new Set(this.queue)];
|
|
183
|
+
|
|
177
184
|
let tracking = { totalFetched: 0, totalNew: 0, count: 0, newCount : 0 };
|
|
178
185
|
// phase 2: query for history & content
|
|
179
186
|
this.requeue = [];
|
|
@@ -418,8 +425,8 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
|
|
|
418
425
|
isRefreshing: this.isRefreshing,
|
|
419
426
|
refreshIntervalHours: this.refreshIntervalHours,
|
|
420
427
|
nextRefresh: this.refreshTimer && this.lastRefresh
|
|
421
|
-
|
|
422
|
-
|
|
428
|
+
? new Date(this.lastRefresh.getTime() + (this.refreshIntervalHours * 60 * 60 * 1000))
|
|
429
|
+
: null
|
|
423
430
|
}
|
|
424
431
|
};
|
|
425
432
|
}
|
|
@@ -506,8 +513,8 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
|
|
|
506
513
|
if (bundle.entry && bundle.entry.length > 0) {
|
|
507
514
|
// Extract ValueSets from bundle entries
|
|
508
515
|
const valueSets = bundle.entry
|
|
509
|
-
|
|
510
|
-
|
|
516
|
+
.filter(entry => entry.resource && entry.resource.resourceType === 'ValueSet')
|
|
517
|
+
.map(entry => entry.resource);
|
|
511
518
|
if (valueSets.length > 0) {
|
|
512
519
|
tracking.totalNew = tracking.totalNew + await this.batchUpsertValueSets(valueSets);
|
|
513
520
|
tracking.totalFetched += valueSets.length;
|
|
@@ -519,6 +526,58 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
|
|
|
519
526
|
this.stats.task('VSAC Sync', logMsg);
|
|
520
527
|
}
|
|
521
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Scan VSAC for recently updated value sets using the _lastUpdated parameter.
|
|
531
|
+
* Uses a stored date from the previous run; if none exists, defaults to 4 days ago.
|
|
532
|
+
* Adds any found URLs to this.queue and stores the server's response date for next time.
|
|
533
|
+
* @returns {Promise<number>} Number of value set URLs added to the queue
|
|
534
|
+
* @private
|
|
535
|
+
*/
|
|
536
|
+
async _scanLastUpdated() {
|
|
537
|
+
const SETTING_KEY = 'vsac_last_updated_date';
|
|
538
|
+
|
|
539
|
+
let sinceDate = await this.database.getSetting(SETTING_KEY);
|
|
540
|
+
if (!sinceDate) {
|
|
541
|
+
// No stored date — default to 10 days ago
|
|
542
|
+
const d = new Date();
|
|
543
|
+
d.setDate(d.getDate() - 10);
|
|
544
|
+
sinceDate = d.toISOString();
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
let url = `/res/ValueSet/?_lastUpdated=ge${sinceDate}&_offset=0&_count=100&_elements=id,url,version,status`;
|
|
548
|
+
let count = 0;
|
|
549
|
+
let serverDate = null;
|
|
550
|
+
|
|
551
|
+
while (url) {
|
|
552
|
+
console.log(`_lastUpdated scan: ${count} found so far`);
|
|
553
|
+
this.stats.task('VSAC Sync', `_lastUpdated scan: ${count} found`);
|
|
554
|
+
|
|
555
|
+
const bundle = await this._fetchBundle(url);
|
|
556
|
+
|
|
557
|
+
// Capture the server's lastUpdated from the first page
|
|
558
|
+
if (!serverDate && bundle.meta && bundle.meta.lastUpdated) {
|
|
559
|
+
serverDate = bundle.meta.lastUpdated;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
for (let be of bundle.entry || []) {
|
|
563
|
+
let vs = be.resource;
|
|
564
|
+
if (vs && vs.url) {
|
|
565
|
+
this.queue.push(vs.url);
|
|
566
|
+
count++;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
url = this._getNextUrl(bundle);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Store the server date for next run
|
|
574
|
+
if (serverDate) {
|
|
575
|
+
await this.database.setSetting(SETTING_KEY, serverDate);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return count;
|
|
579
|
+
}
|
|
580
|
+
|
|
522
581
|
name() {
|
|
523
582
|
return "VSAC";
|
|
524
583
|
}
|
|
@@ -533,38 +592,38 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
|
|
|
533
592
|
|
|
534
593
|
const rows = await new Promise((resolve, reject) => {
|
|
535
594
|
db.all(
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
595
|
+
`SELECT 'vs' AS kind,
|
|
596
|
+
url,
|
|
597
|
+
version,
|
|
598
|
+
date_first_seen AS ts,
|
|
599
|
+
NULL AS status,
|
|
600
|
+
NULL AS error_message,
|
|
601
|
+
NULL AS finished_at,
|
|
602
|
+
NULL AS total_fetched,
|
|
603
|
+
NULL AS total_new
|
|
545
604
|
FROM valuesets
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
605
|
+
WHERE date_first_seen > 0
|
|
606
|
+
UNION ALL
|
|
607
|
+
SELECT 'run' AS kind,
|
|
608
|
+
NULL,
|
|
609
|
+
NULL,
|
|
610
|
+
started_at AS ts,
|
|
611
|
+
status,
|
|
612
|
+
error_message,
|
|
613
|
+
finished_at,
|
|
614
|
+
total_fetched,
|
|
615
|
+
total_new
|
|
557
616
|
FROM vsac_runs
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
617
|
+
ORDER BY ts DESC
|
|
618
|
+
LIMIT 200`,
|
|
619
|
+
[],
|
|
620
|
+
(err, rows) => err ? reject(err) : resolve(rows)
|
|
562
621
|
);
|
|
563
622
|
});
|
|
564
623
|
|
|
565
624
|
const fmt = ts => ts
|
|
566
|
-
|
|
567
|
-
|
|
625
|
+
? new Date(ts * 1000).toISOString().replace('T', ' ').substring(0, 19) + ' UTC'
|
|
626
|
+
: '—';
|
|
568
627
|
|
|
569
628
|
let html = '<h3>VSAC Sync History</h3>';
|
|
570
629
|
html += '<table class="grid">';
|
package/tx/workers/expand.js
CHANGED
|
@@ -646,6 +646,10 @@ class ValueSetExpander {
|
|
|
646
646
|
throw new Issue('error', 'business-rule', null, null, 'The code system definition for ' + cset.system + ' has no content, so this expansion cannot be performed', 'invalid');
|
|
647
647
|
} else if (cs.contentMode() === 'supplement') {
|
|
648
648
|
throw new Issue('error', 'business-rule', null, null, 'The code system definition for ' + cset.system + ' defines a supplement, so this expansion cannot be performed', 'invalid');
|
|
649
|
+
} else if (cs.contentMode() === 'fragment') {
|
|
650
|
+
this.addParamUri(exp, 'used-fragment', cs.system() + '|' + cs.version());
|
|
651
|
+
Extensions.addBoolean(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
|
|
652
|
+
Extensions.addString(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason","This extension is based on a fragment of the code system " + cset.system);
|
|
649
653
|
} else {
|
|
650
654
|
this.addParamUri(exp, cs.contentMode(), cs.system() + '|' + cs.version());
|
|
651
655
|
Extensions.addBoolean(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
|
|
@@ -1010,7 +1014,7 @@ class ValueSetExpander {
|
|
|
1010
1014
|
cds.clear();
|
|
1011
1015
|
Extensions.checkNoModifiers(cc, 'ValueSetExpander.processCodes', 'set concept reference', vsSrc.vurl);
|
|
1012
1016
|
const cctxt = await cs.locate(cc.code, this.allAltCodes);
|
|
1013
|
-
if (cctxt && cctxt.context && (!this.params.activeOnly || !await cs.isInactive(cctxt)) && await this.passesFilters(cs, cctxt, prep, filters, 0)) {
|
|
1017
|
+
if (cctxt && cctxt.context && (!this.params.activeOnly || !await cs.isInactive(cctxt.context)) && await this.passesFilters(cs, cctxt.context, prep, filters, 0)) {
|
|
1014
1018
|
if (filter.passesDesignations(cds) || filter.passes(cc.code)) {
|
|
1015
1019
|
let ov = Extensions.readString(cc, 'http://hl7.org/fhir/StructureDefinition/itemWeight');
|
|
1016
1020
|
if (!ov) {
|
package/tx/workers/metadata.js
CHANGED
|
@@ -232,7 +232,8 @@ class MetadataHandler {
|
|
|
232
232
|
],
|
|
233
233
|
operation: [
|
|
234
234
|
{ name: 'expand', definition: 'http://hl7.org/fhir/OperationDefinition/ValueSet-expand' },
|
|
235
|
-
{ name: 'validate-code', definition: 'http://hl7.org/fhir/OperationDefinition/ValueSet-validate-code' }
|
|
235
|
+
{ name: 'validate-code', definition: 'http://hl7.org/fhir/OperationDefinition/ValueSet-validate-code' },
|
|
236
|
+
{ name: 'related', definition: 'https://raw.githubusercontent.com/HealthIntersections/FHIRsmith/refs/heads/main/tx/data/OperationDefinition-ValueSet-related.json' }
|
|
236
237
|
]
|
|
237
238
|
},
|
|
238
239
|
{
|
|
@@ -265,6 +266,7 @@ class MetadataHandler {
|
|
|
265
266
|
{ name: 'validate-code', definition: 'http://hl7.org/fhir/OperationDefinition/Resource-validate-code' },
|
|
266
267
|
{ name: 'translate', definition: 'http://hl7.org/fhir/OperationDefinition/ConceptMap-translate' },
|
|
267
268
|
{ name: 'closure', definition: 'http://hl7.org/fhir/OperationDefinition/ConceptMap-closure' },
|
|
269
|
+
{ name: 'related', definition: 'https://raw.githubusercontent.com/HealthIntersections/FHIRsmith/refs/heads/main/tx/data/OperationDefinition-ValueSet-related.json' },
|
|
268
270
|
{ name: 'versions', definition: 'http://hl7.org/fhir/OperationDefinition/fhir-versions' }
|
|
269
271
|
]
|
|
270
272
|
}
|