fhirsmith 0.7.6 → 0.8.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 +48 -0
- package/README.md +5 -1
- package/library/languages.js +10 -0
- package/package.json +1 -1
- package/packages/package-crawler.js +2 -2
- package/publisher/publisher.js +1 -1
- package/registry/registry.js +2 -2
- package/root-bare-template.html +1 -2
- package/security.md +3 -0
- package/server.js +100 -70
- package/stats.js +37 -6
- package/tx/cs/cs-api.js +8 -4
- package/tx/cs/cs-loinc.js +14 -2
- package/tx/cs/cs-omop.js +5 -3
- package/tx/cs/cs-rxnorm.js +18 -16
- package/tx/cs/cs-snomed.js +279 -6
- package/tx/data/cpt-fragment.db +0 -0
- package/tx/data/cs-de.json +186 -0
- package/tx/data/cs-extensions.json +92 -0
- package/tx/data/cs-simple.json +130 -0
- package/tx/data/cs-supplement.json +78 -0
- package/tx/data/lang.dat +49180 -0
- package/tx/data/languages.csv +191 -0
- package/tx/data/loinc-subset.txt +75 -0
- package/tx/data/omop-fragment.db +0 -0
- package/tx/data/readme.md +43 -0
- package/tx/data/regions.csv +273 -0
- package/tx/data/rxnorm-subset.txt +22 -0
- package/tx/data/snomed-subset.txt +47 -0
- package/tx/data/ucum-essence.xml +2059 -0
- package/tx/html/dash-metrics.liquid +147 -0
- package/tx/importers/import-rxnorm.module.js +4 -30
- package/tx/library/canonical-resource.js +8 -0
- package/tx/library/conceptmap.js +29 -1
- package/tx/library/designations.js +4 -8
- package/tx/library/extensions.js +4 -3
- package/tx/library/renderer.js +9 -9
- package/tx/ocl/cm-ocl.cjs +185 -65
- package/tx/ocl/cs-ocl.cjs +69 -50
- package/tx/ocl/jobs/background-queue.cjs +0 -8
- package/tx/ocl/mappers/concept-mapper.cjs +13 -3
- package/tx/ocl/shared/patches.cjs +1 -0
- package/tx/ocl/vs-ocl.cjs +137 -157
- package/tx/operation-context.js +3 -3
- package/tx/params.js +2 -2
- package/tx/provider.js +6 -3
- package/tx/sct/structures.js +6 -1
- package/tx/tx.fhir.org.yml +1 -1
- package/tx/vs/vs-database.js +107 -23
- package/tx/vs/vs-vsac.js +66 -19
- package/tx/workers/expand.js +10 -10
- package/tx/workers/related.js +2 -2
- package/tx/workers/search.js +2 -1
- package/tx/workers/translate.js +222 -33
- package/tx/workers/validate.js +13 -13
- package/tx/xversion/xv-parameters.js +54 -1
- package/xig/xig.js +171 -9
package/tx/cs/cs-rxnorm.js
CHANGED
|
@@ -331,20 +331,20 @@ class RxNormServices extends CodeSystemProvider {
|
|
|
331
331
|
async doesFilter(prop, op, value) {
|
|
332
332
|
|
|
333
333
|
|
|
334
|
-
|
|
334
|
+
let propUC = prop.toUpperCase();
|
|
335
335
|
|
|
336
336
|
// TTY filters
|
|
337
|
-
if (
|
|
337
|
+
if (propUC === 'TTY' && ['=', 'in'].includes(op)) {
|
|
338
338
|
return true;
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
// STY filter
|
|
342
|
-
if (
|
|
342
|
+
if (propUC === 'STY' && op === '=') {
|
|
343
343
|
return true;
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
// SAB filter
|
|
347
|
-
if (
|
|
347
|
+
if (propUC === 'SAB' && op === '=') {
|
|
348
348
|
return true;
|
|
349
349
|
}
|
|
350
350
|
|
|
@@ -370,12 +370,12 @@ class RxNormServices extends CodeSystemProvider {
|
|
|
370
370
|
|
|
371
371
|
|
|
372
372
|
const filter = new RxNormFilterHolder();
|
|
373
|
-
|
|
373
|
+
let propUC = prop.toUpperCase();
|
|
374
374
|
|
|
375
375
|
let sql = '';
|
|
376
376
|
let params = {};
|
|
377
377
|
|
|
378
|
-
if (op === 'in' &&
|
|
378
|
+
if (op === 'in' && propUC === 'TTY') {
|
|
379
379
|
const values = value.split(',').map(v => v.trim()).filter(v => v);
|
|
380
380
|
const placeholders = values.map((_, i) => `$tty${i}`).join(',');
|
|
381
381
|
sql = `AND TTY IN (${placeholders})`;
|
|
@@ -383,36 +383,36 @@ class RxNormServices extends CodeSystemProvider {
|
|
|
383
383
|
params[`tty${i}`] = this.#sqlWrapString(val);
|
|
384
384
|
});
|
|
385
385
|
} else if (op === '=') {
|
|
386
|
-
if (
|
|
386
|
+
if (propUC === 'STY') {
|
|
387
387
|
sql = `AND ${this.getCodeField()} IN (SELECT RXCUI FROM rxnsty WHERE TUI = $sty)`;
|
|
388
388
|
params.sty = this.#sqlWrapString(value);
|
|
389
|
-
} else if (
|
|
389
|
+
} else if (propUC === 'SAB') {
|
|
390
390
|
sql = `AND ${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE SAB = $sab)`;
|
|
391
391
|
params.sab = this.#sqlWrapString(value);
|
|
392
|
-
} else if (
|
|
392
|
+
} else if (propUC === 'TTY') {
|
|
393
393
|
sql = `AND TTY = $tty`;
|
|
394
394
|
params.tty = this.#sqlWrapString(value);
|
|
395
395
|
} else if (this.rels.includes(prop)) {
|
|
396
396
|
if (value.startsWith('CUI:')) {
|
|
397
397
|
const cui = value.substring(4);
|
|
398
|
-
sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXCUI IN (SELECT
|
|
398
|
+
sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXCUI IN (SELECT RXCUI2 FROM rxnrel WHERE REL = $rel AND RXCUI1 = $cui2)))`;
|
|
399
399
|
params.rel = this.#sqlWrapString(prop);
|
|
400
400
|
params.cui2 = this.#sqlWrapString(cui);
|
|
401
401
|
} else if (value.startsWith('AUI:')) {
|
|
402
402
|
const aui = value.substring(4);
|
|
403
|
-
sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXAUI IN (SELECT
|
|
403
|
+
sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXAUI IN (SELECT RXAUI2 FROM rxnrel WHERE REL = $rel AND RXAUI1 = $aui2)))`;
|
|
404
404
|
params.rel = this.#sqlWrapString(prop);
|
|
405
405
|
params.aui2 = this.#sqlWrapString(aui);
|
|
406
406
|
}
|
|
407
407
|
} else if (this.reltypes.includes(prop)) {
|
|
408
408
|
if (value.startsWith('CUI:')) {
|
|
409
409
|
const cui = value.substring(4);
|
|
410
|
-
sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXCUI IN (SELECT
|
|
410
|
+
sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXCUI IN (SELECT RXCUI2 FROM rxnrel WHERE RELA = $rela AND RXCUI1 = $cui2)))`;
|
|
411
411
|
params.rela = this.#sqlWrapString(prop);
|
|
412
412
|
params.cui2 = this.#sqlWrapString(cui);
|
|
413
413
|
} else if (value.startsWith('AUI:')) {
|
|
414
414
|
const aui = value.substring(4);
|
|
415
|
-
sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXAUI IN (SELECT
|
|
415
|
+
sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXAUI IN (SELECT RXAUI2 FROM rxnrel WHERE RELA = $rela AND RXAUI1 = $aui2)))`;
|
|
416
416
|
params.rela = this.#sqlWrapString(prop);
|
|
417
417
|
params.aui2 = this.#sqlWrapString(aui);
|
|
418
418
|
}
|
|
@@ -429,7 +429,6 @@ class RxNormServices extends CodeSystemProvider {
|
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
async searchFilter(filterContext, filter, sort) {
|
|
432
|
-
|
|
433
432
|
|
|
434
433
|
if (!filter || !filter.stems || filter.stems.length === 0) {
|
|
435
434
|
throw new Error('Invalid search filter');
|
|
@@ -499,8 +498,6 @@ class RxNormServices extends CodeSystemProvider {
|
|
|
499
498
|
}
|
|
500
499
|
|
|
501
500
|
async filterSize(filterContext, set) {
|
|
502
|
-
|
|
503
|
-
|
|
504
501
|
if (!set.executed) {
|
|
505
502
|
await this.#executeFilter(set);
|
|
506
503
|
}
|
|
@@ -709,6 +706,11 @@ class RxNormTypeServicesFactory extends CodeSystemFactoryProvider {
|
|
|
709
706
|
const db = new sqlite3.Database(this.dbPath);
|
|
710
707
|
|
|
711
708
|
try {
|
|
709
|
+
await new Promise((resolve, reject) => {
|
|
710
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_rxnstems_cui_stem ON RXNSTEMS(CUI, stem)`,
|
|
711
|
+
err => err ? reject(err) : resolve());
|
|
712
|
+
});
|
|
713
|
+
|
|
712
714
|
this._sharedData = {
|
|
713
715
|
version: '',
|
|
714
716
|
rels: [],
|
package/tx/cs/cs-snomed.js
CHANGED
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
const {DesignationUse} = require("../library/designations");
|
|
13
13
|
const {BaseCSServices} = require("./cs-base");
|
|
14
14
|
const {formatDateMMDDYYYY} = require("../../library/utilities");
|
|
15
|
+
const {ConceptMap} = require("../library/conceptmap");
|
|
15
16
|
|
|
16
17
|
// Context kinds matching Pascal enum
|
|
17
18
|
const SnomedProviderContextKind = {
|
|
@@ -371,6 +372,29 @@ class SnomedServices {
|
|
|
371
372
|
return result;
|
|
372
373
|
}
|
|
373
374
|
|
|
375
|
+
filterInactive(state) {
|
|
376
|
+
const result = new SnomedFilterContext();
|
|
377
|
+
result.inactive = state;
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
filterModuleId(id) {
|
|
382
|
+
const result = new SnomedFilterContext();
|
|
383
|
+
let concept = this.concepts.findConcept(id);
|
|
384
|
+
result.moduleId = concept.index;
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
filterByProperty(prop, value) {
|
|
389
|
+
const result = new SnomedFilterContext();
|
|
390
|
+
let p = this.concepts.findConcept(prop);
|
|
391
|
+
let v = this.concepts.findConcept(value);
|
|
392
|
+
result.propProp = p.index;
|
|
393
|
+
result.propValue = v.index;
|
|
394
|
+
return result;
|
|
395
|
+
|
|
396
|
+
}
|
|
397
|
+
|
|
374
398
|
searchFilter(searchText, includeInactive = false, exactMatch = false) {
|
|
375
399
|
const result = new SnomedFilterContext();
|
|
376
400
|
|
|
@@ -765,6 +789,12 @@ class SnomedProvider extends BaseCSServices {
|
|
|
765
789
|
this._addCodeProperty(params, 'property', 'child', code, null, description);
|
|
766
790
|
}
|
|
767
791
|
|
|
792
|
+
const moduleId = this.sct.concepts.getModuleId(ctxt.getReference());
|
|
793
|
+
if (moduleId) {
|
|
794
|
+
const code = this.sct.getConceptId(moduleId);
|
|
795
|
+
this._addCodeProperty(params, 'property', 'module', code, null, null);
|
|
796
|
+
}
|
|
797
|
+
|
|
768
798
|
const relationships = this.sct.getConceptRelationships(ctxt.getReference());
|
|
769
799
|
let set = new Set();
|
|
770
800
|
for (let relationshipRef of relationships) {
|
|
@@ -812,10 +842,25 @@ class SnomedProvider extends BaseCSServices {
|
|
|
812
842
|
return this.sct.conceptExists(value);
|
|
813
843
|
}
|
|
814
844
|
}
|
|
845
|
+
if (prop === 'inactive') {
|
|
846
|
+
return op === '=' && ['true', 'false'].includes(value);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (prop === 'moduleId') {
|
|
850
|
+
const id = this.sct.stringToIdOrZero(value);
|
|
851
|
+
return id !== 0n && op === '=';
|
|
852
|
+
}
|
|
853
|
+
|
|
815
854
|
if (prop == 'expressions' && op == '=' && ['true', 'false'].includes(value)) {
|
|
816
855
|
return true;
|
|
817
856
|
}
|
|
818
857
|
|
|
858
|
+
const cid = this.sct.stringToIdOrZero(prop);
|
|
859
|
+
if (cid != 0) {
|
|
860
|
+
const id = this.sct.stringToIdOrZero(value);
|
|
861
|
+
return id !== 0n && op === '=';
|
|
862
|
+
}
|
|
863
|
+
|
|
819
864
|
return false;
|
|
820
865
|
}
|
|
821
866
|
|
|
@@ -851,7 +896,38 @@ class SnomedProvider extends BaseCSServices {
|
|
|
851
896
|
return null;
|
|
852
897
|
}
|
|
853
898
|
default:
|
|
854
|
-
throw new Error(`Unsupported filter operation: ${op}`);
|
|
899
|
+
throw new Error(`Unsupported filter operation: concept ${op} ${value}`);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (prop === 'inactive') {
|
|
904
|
+
if (value !== 'true' && value !== 'false') {
|
|
905
|
+
throw new Error(`Invalid filter value: ${value}`);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
switch (op) {
|
|
909
|
+
case '=': {
|
|
910
|
+
filterContext.filters.push(this.sct.filterInactive(value === 'true'));
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
default:
|
|
914
|
+
throw new Error(`Unsupported filter operation: inactive ${op} ${value}`);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (prop === 'moduleId') {
|
|
919
|
+
const id = this.sct.stringToIdOrZero(value);
|
|
920
|
+
if (id === 0n) {
|
|
921
|
+
throw new Error(`Invalid concept ID: ${value}`);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
switch (op) {
|
|
925
|
+
case '=': {
|
|
926
|
+
filterContext.filters.push(this.sct.filterModuleId(id));
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
default:
|
|
930
|
+
throw new Error(`Unsupported filter operation: moduleId ${op} ${value}`);
|
|
855
931
|
}
|
|
856
932
|
}
|
|
857
933
|
|
|
@@ -862,6 +938,24 @@ class SnomedProvider extends BaseCSServices {
|
|
|
862
938
|
return null;
|
|
863
939
|
}
|
|
864
940
|
|
|
941
|
+
const cid = this.sct.stringToIdOrZero(prop);
|
|
942
|
+
if (cid != 0) {
|
|
943
|
+
|
|
944
|
+
const id = this.sct.stringToIdOrZero(value);
|
|
945
|
+
if (id === 0n) {
|
|
946
|
+
throw new Error(`Invalid concept ID: ${value}`);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
switch (op) {
|
|
950
|
+
case '=': {
|
|
951
|
+
filterContext.filters.push(this.sct.filterByProperty(cid, id));
|
|
952
|
+
return null;
|
|
953
|
+
}
|
|
954
|
+
default:
|
|
955
|
+
throw new Error(`Unsupported filter operation: ${prop} ${op} ${value}`);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
865
959
|
throw new Error(`Unsupported filter property: ${prop}`);
|
|
866
960
|
}
|
|
867
961
|
|
|
@@ -893,7 +987,7 @@ class SnomedProvider extends BaseCSServices {
|
|
|
893
987
|
|
|
894
988
|
async filterMore(filterContext, set) {
|
|
895
989
|
set.cursor = set.cursor || 0;
|
|
896
|
-
|
|
990
|
+
this.#ensurePopulated(set);
|
|
897
991
|
const size = await this.filterSize(filterContext, set);
|
|
898
992
|
return set.cursor < size;
|
|
899
993
|
}
|
|
@@ -927,12 +1021,27 @@ class SnomedProvider extends BaseCSServices {
|
|
|
927
1021
|
}
|
|
928
1022
|
|
|
929
1023
|
const ctxt = conceptResult.context;
|
|
930
|
-
|
|
931
|
-
|
|
932
1024
|
const reference = ctxt.getReference();
|
|
933
1025
|
let found = false;
|
|
934
1026
|
|
|
935
|
-
if (set.
|
|
1027
|
+
if (set.inactive !== undefined) {
|
|
1028
|
+
let concept = this.sct.concepts.getConcept(reference);
|
|
1029
|
+
let active = (concept.flags & 0x0F) === 0;
|
|
1030
|
+
found = active !== set.inactive
|
|
1031
|
+
} else if (set.moduleId) {
|
|
1032
|
+
let concept = this.sct.concepts.getConcept(reference);
|
|
1033
|
+
let moduleId = this.sct.concepts.getModuleId(concept.index);
|
|
1034
|
+
found = moduleId === set.moduleId;
|
|
1035
|
+
} else if (set.propProp || set.propValue) {
|
|
1036
|
+
found = false;
|
|
1037
|
+
const relationships = this.sct.getConceptRelationships(reference);
|
|
1038
|
+
for (let relationshipRef of relationships) {
|
|
1039
|
+
const relationship = this.sct.relationships.getRelationship(relationshipRef);
|
|
1040
|
+
if (set.propProp === relationship.relType && set.propValue === relationship.target) {
|
|
1041
|
+
found = true;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
} else if (set.matches && set.matches.length > 0) {
|
|
936
1045
|
found = set.matches.some(m => m.index === reference);
|
|
937
1046
|
} else if (set.members && set.members.length > 0) {
|
|
938
1047
|
found = set.members.some(m => m.ref === reference);
|
|
@@ -958,6 +1067,23 @@ class SnomedProvider extends BaseCSServices {
|
|
|
958
1067
|
}
|
|
959
1068
|
|
|
960
1069
|
const reference = concept.getReference();
|
|
1070
|
+
if (set.inactive !== undefined) {
|
|
1071
|
+
return this.sct.isActive(reference) !== set.inactive;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
if (set.moduleId) {
|
|
1075
|
+
return this.sct.concepts.getModuleId(reference) === set.moduleId;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (set.propProp || set.propValue) {
|
|
1079
|
+
const relationships = this.sct.getConceptRelationships(reference);
|
|
1080
|
+
for (let relationshipRef of relationships) {
|
|
1081
|
+
const relationship = this.sct.relationships.getRelationship(relationshipRef);
|
|
1082
|
+
if (set.propProp === relationship.relType && set.propValue === relationship.target) {
|
|
1083
|
+
return true;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
961
1087
|
|
|
962
1088
|
if (set.matches && set.matches.length > 0) {
|
|
963
1089
|
return set.matches.some(m => m.index === reference);
|
|
@@ -970,6 +1096,42 @@ class SnomedProvider extends BaseCSServices {
|
|
|
970
1096
|
return false;
|
|
971
1097
|
}
|
|
972
1098
|
|
|
1099
|
+
#ensurePopulated(set) {
|
|
1100
|
+
if (set.populationDone) {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
if (set.inactive !== undefined && set.descendants.length === 0) {
|
|
1104
|
+
for (let i = 0; i < this.sct.concepts.count(); i++) {
|
|
1105
|
+
let concept = this.sct.concepts.getConceptByCount(i);
|
|
1106
|
+
let active = (concept.flags & 0x0F) === 0;
|
|
1107
|
+
if (active !== set.inactive) {
|
|
1108
|
+
set.descendants.push(concept.index);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
if (set.moduleId) {
|
|
1113
|
+
for (let i = 0; i < this.sct.concepts.count(); i++) {
|
|
1114
|
+
let concept = this.sct.concepts.getConceptByCount(i);
|
|
1115
|
+
let moduleId = this.sct.concepts.getModuleId(concept.index);
|
|
1116
|
+
if (moduleId === set.moduleId) {
|
|
1117
|
+
set.descendants.push(concept.index);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (set.propProp || set.propValue) {
|
|
1122
|
+
for (let i = 0; i < this.sct.concepts.count(); i++) {
|
|
1123
|
+
let concept = this.sct.concepts.getConceptByCount(i);
|
|
1124
|
+
const relationships = this.sct.getConceptRelationships(concept.getReference());
|
|
1125
|
+
for (let relationshipRef of relationships) {
|
|
1126
|
+
const relationship = this.sct.relationships.getRelationship(relationshipRef);
|
|
1127
|
+
if (set.propProp === relationship.relType && set.propValue === relationship.target) {
|
|
1128
|
+
set.descendants.push(concept.index);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
set.populationDone = true;
|
|
1134
|
+
}
|
|
973
1135
|
|
|
974
1136
|
// Search filter
|
|
975
1137
|
async searchFilter(filterContext, filter, sort) {
|
|
@@ -1052,6 +1214,64 @@ class SnomedProvider extends BaseCSServices {
|
|
|
1052
1214
|
(cd.use.code === '900000000000013009' || cd.use.code === '900000000000003001');
|
|
1053
1215
|
}
|
|
1054
1216
|
|
|
1217
|
+
async getTranslations(map, coding, target, reverse) {
|
|
1218
|
+
if (!map || (target && target !== this.system()) || reverse) {
|
|
1219
|
+
return [];
|
|
1220
|
+
}
|
|
1221
|
+
let ref = this.sct.concepts.findConcept(map.id);
|
|
1222
|
+
if (!ref.found) {
|
|
1223
|
+
return [];
|
|
1224
|
+
}
|
|
1225
|
+
let rref = this.sct.refSetIndex.getRefSetByConcept(ref.index);
|
|
1226
|
+
if (rref == -1) {
|
|
1227
|
+
return [];
|
|
1228
|
+
}
|
|
1229
|
+
let refSetRecord = this.sct.refSetIndex.getReferenceSet(rref);
|
|
1230
|
+
let members = this.sct.refSetMembers.getMembers(refSetRecord.membersByRef);
|
|
1231
|
+
let srcConcept = this.sct.concepts.findConcept(coding.code);
|
|
1232
|
+
if (!srcConcept.found) {
|
|
1233
|
+
return [];
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
let result = [];
|
|
1237
|
+
let L = 0;
|
|
1238
|
+
let H = members.length - 1;
|
|
1239
|
+
while (L <= H) {
|
|
1240
|
+
const I = Math.floor((L + H) / 2);
|
|
1241
|
+
const ref = members[I].ref;
|
|
1242
|
+
if (ref < srcConcept.index) {
|
|
1243
|
+
L = I + 1;
|
|
1244
|
+
} else if (ref > srcConcept.index) {
|
|
1245
|
+
H = I - 1;
|
|
1246
|
+
} else {
|
|
1247
|
+
// Found — but scan left for first match in case of duplicates
|
|
1248
|
+
let first = I;
|
|
1249
|
+
while (first > 0 && members[first - 1].ref === srcConcept.index) {
|
|
1250
|
+
first--;
|
|
1251
|
+
}
|
|
1252
|
+
// Process all matching members
|
|
1253
|
+
for (let i = first; i < members.length && members[i].ref === srcConcept.index; i++) {
|
|
1254
|
+
let values = this.sct.refs.getReferences(members[i].values);
|
|
1255
|
+
if (values && values.length >= 1) {
|
|
1256
|
+
let tgtId = String(this.sct.concepts.getConceptId(values[0]));
|
|
1257
|
+
let ct = {
|
|
1258
|
+
map: map.vurl,
|
|
1259
|
+
code: tgtId,
|
|
1260
|
+
system: this.system(),
|
|
1261
|
+
version : this.version(),
|
|
1262
|
+
display: await this.display(tgtId),
|
|
1263
|
+
relationship: map.jsonObj.relationship
|
|
1264
|
+
}
|
|
1265
|
+
result.push(ct);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
break;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
return result;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1055
1275
|
}
|
|
1056
1276
|
|
|
1057
1277
|
/**
|
|
@@ -1178,7 +1398,7 @@ class SnomedServicesFactory extends CodeSystemFactoryProvider {
|
|
|
1178
1398
|
return null;
|
|
1179
1399
|
}
|
|
1180
1400
|
let rref = this.snomedServices.refSetIndex.getRefSetByConcept(ref.index);
|
|
1181
|
-
if (rref ==
|
|
1401
|
+
if (rref == -1) {
|
|
1182
1402
|
return null;
|
|
1183
1403
|
}
|
|
1184
1404
|
return {
|
|
@@ -1287,6 +1507,59 @@ class SnomedServicesFactory extends CodeSystemFactoryProvider {
|
|
|
1287
1507
|
|
|
1288
1508
|
return edition + ' ' + formatDateMMDDYYYY(match[2].substring(4, 6) + match[2].substring(6, 8) + match[2].substring(0, 4));
|
|
1289
1509
|
}
|
|
1510
|
+
|
|
1511
|
+
async findImplicitConceptMap(url, version) {
|
|
1512
|
+
if (version && (version !== this.version())) {
|
|
1513
|
+
return null;
|
|
1514
|
+
}
|
|
1515
|
+
if (!url || !url.startsWith(this.system()+"?fhir_cm=")) {
|
|
1516
|
+
return null;
|
|
1517
|
+
}
|
|
1518
|
+
let id = url.substring(url.indexOf("=")+1);
|
|
1519
|
+
if (['900000000000523009', '900000000000526001', '900000000000527005', '900000000000530003'].includes(id)) {
|
|
1520
|
+
let name = '';
|
|
1521
|
+
let relationship = '';
|
|
1522
|
+
switch (id) {
|
|
1523
|
+
case '900000000000523009':
|
|
1524
|
+
name = 'POSSIBLY EQUIVALENT TO';
|
|
1525
|
+
relationship = 'inexact';
|
|
1526
|
+
break;
|
|
1527
|
+
case '900000000000526001':
|
|
1528
|
+
name = 'REPLACED BY';
|
|
1529
|
+
relationship = 'equivalent';
|
|
1530
|
+
break;
|
|
1531
|
+
case '900000000000527005':
|
|
1532
|
+
name = 'SAME AS';
|
|
1533
|
+
relationship = 'equal';
|
|
1534
|
+
break;
|
|
1535
|
+
case '900000000000530003':
|
|
1536
|
+
name = 'ALTERNATIVE';
|
|
1537
|
+
relationship = 'inexact';
|
|
1538
|
+
break;
|
|
1539
|
+
}
|
|
1540
|
+
let cm = {
|
|
1541
|
+
resourceType: 'ConceptMap',
|
|
1542
|
+
internalSource : this,
|
|
1543
|
+
relationship: relationship,
|
|
1544
|
+
id : id,
|
|
1545
|
+
url: `${this.system}?fhir_cm=${id}`,
|
|
1546
|
+
version: this.version(),
|
|
1547
|
+
name: `SNOMED CT ${name} Concept Map`,
|
|
1548
|
+
description: `The concept map implicitly defined by the ${name} Association Reference Set`,
|
|
1549
|
+
copyright: 'This value set includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (SNOMED International), and distributed by agreement between SNOMED International and HL7',
|
|
1550
|
+
status: 'active',
|
|
1551
|
+
sourceUri: `${this.system}?fhir_vs`,
|
|
1552
|
+
targetUri: `${this.system}?fhir_vs`,
|
|
1553
|
+
group: [{
|
|
1554
|
+
source: 'http://snomed.info/sct',
|
|
1555
|
+
target: 'http://snomed.info/sct'
|
|
1556
|
+
}]
|
|
1557
|
+
}
|
|
1558
|
+
return new ConceptMap(cm);
|
|
1559
|
+
} else {
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1290
1563
|
}
|
|
1291
1564
|
|
|
1292
1565
|
function getEditionName(edition) {
|
|
Binary file
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"resourceType" : "CodeSystem",
|
|
3
|
+
"language" : "de",
|
|
4
|
+
"id" : "de-multi",
|
|
5
|
+
"url" : "http://hl7.org/fhir/test/CodeSystem/de-multi",
|
|
6
|
+
"name" : "TestCodeSystemMultiLangDE",
|
|
7
|
+
"title" : "Testcodesystem mit mehreren Sprachen",
|
|
8
|
+
"title:en" : "Test Code System with Multiple Languages",
|
|
9
|
+
"_title" : {
|
|
10
|
+
"extension" : [{
|
|
11
|
+
"url" : "http://hl7.org/fhir/StructureDefinition/translation",
|
|
12
|
+
"extension" : [{
|
|
13
|
+
"url" : "lang",
|
|
14
|
+
"valueCode" : "en"
|
|
15
|
+
},{
|
|
16
|
+
"url" : "content",
|
|
17
|
+
"valueString" : "Test Code System with Multiple Languages"
|
|
18
|
+
}]
|
|
19
|
+
}]
|
|
20
|
+
},
|
|
21
|
+
"status" : "active",
|
|
22
|
+
"experimental" : false,
|
|
23
|
+
"date" : "2023-04-01",
|
|
24
|
+
"publisher" : "FHIR Project",
|
|
25
|
+
"caseSensitive" : true,
|
|
26
|
+
"content" : "complete",
|
|
27
|
+
"concept" : [{
|
|
28
|
+
"code" : "code1",
|
|
29
|
+
"display" : "Anzeige 1",
|
|
30
|
+
"definition" : "Mein erster Code",
|
|
31
|
+
"definition:en" : "My first code",
|
|
32
|
+
"_definition" : {
|
|
33
|
+
"extension" : [{
|
|
34
|
+
"url" : "http://hl7.org/fhir/StructureDefinition/translation",
|
|
35
|
+
"extension" : [{
|
|
36
|
+
"url" : "lang",
|
|
37
|
+
"valueCode" : "en"
|
|
38
|
+
},{
|
|
39
|
+
"url" : "content",
|
|
40
|
+
"valueString" : "My first code"
|
|
41
|
+
}]
|
|
42
|
+
}]
|
|
43
|
+
},
|
|
44
|
+
"designation" : [{
|
|
45
|
+
"language" : "en",
|
|
46
|
+
"value" : "Display 1"
|
|
47
|
+
}]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"code" : "code2",
|
|
51
|
+
"display" : "Anzeige 2",
|
|
52
|
+
"definition" : "Mein zweiter Code, mit Kindern",
|
|
53
|
+
"definition:en" : "My second code, with children",
|
|
54
|
+
"_definition" : {
|
|
55
|
+
"extension" : [{
|
|
56
|
+
"url" : "http://hl7.org/fhir/StructureDefinition/translation",
|
|
57
|
+
"extension" : [{
|
|
58
|
+
"url" : "lang",
|
|
59
|
+
"valueCode" : "en"
|
|
60
|
+
},{
|
|
61
|
+
"url" : "content",
|
|
62
|
+
"valueString" : "My second code, with children"
|
|
63
|
+
}]
|
|
64
|
+
}]
|
|
65
|
+
},
|
|
66
|
+
"designation" : [{
|
|
67
|
+
"language" : "de-CH",
|
|
68
|
+
"value" : "Anzeige 2"
|
|
69
|
+
},{
|
|
70
|
+
"language" : "es",
|
|
71
|
+
"value" : "Mostrar 2"
|
|
72
|
+
},{
|
|
73
|
+
"language" : "en",
|
|
74
|
+
"value" : "Display 2"
|
|
75
|
+
}],
|
|
76
|
+
"concept" : [{
|
|
77
|
+
"code" : "code2a",
|
|
78
|
+
"display" : "Anzeige 2a",
|
|
79
|
+
"definition" : "Mein erster Second-Level-Code",
|
|
80
|
+
"definition:en" : "My first second level code",
|
|
81
|
+
"_definition" : {
|
|
82
|
+
"extension" : [{
|
|
83
|
+
"url" : "http://hl7.org/fhir/StructureDefinition/translation",
|
|
84
|
+
"extension" : [{
|
|
85
|
+
"url" : "lang",
|
|
86
|
+
"valueCode" : "en"
|
|
87
|
+
},{
|
|
88
|
+
"url" : "content",
|
|
89
|
+
"valueString" : "My first second level code"
|
|
90
|
+
}]
|
|
91
|
+
}]
|
|
92
|
+
},
|
|
93
|
+
"designation" : [{
|
|
94
|
+
"language" : "en",
|
|
95
|
+
"value" : "Display 2a"
|
|
96
|
+
},{
|
|
97
|
+
"language" : "es",
|
|
98
|
+
"value" : "Mostrar 2a"
|
|
99
|
+
}],
|
|
100
|
+
"concept" : [{
|
|
101
|
+
"code" : "code2aI",
|
|
102
|
+
"display" : "Anzeige 2aI",
|
|
103
|
+
"definition" : "Mein erster Third-Level-Code",
|
|
104
|
+
"definition:en" : "My first third level code",
|
|
105
|
+
"_definition" : {
|
|
106
|
+
"extension" : [{
|
|
107
|
+
"url" : "http://hl7.org/fhir/StructureDefinition/translation",
|
|
108
|
+
"extension" : [{
|
|
109
|
+
"url" : "lang",
|
|
110
|
+
"valueCode" : "en"
|
|
111
|
+
},{
|
|
112
|
+
"url" : "content",
|
|
113
|
+
"valueString" : "My first third level code"
|
|
114
|
+
}]
|
|
115
|
+
}]
|
|
116
|
+
},
|
|
117
|
+
"designation" : [{
|
|
118
|
+
"language" : "es",
|
|
119
|
+
"value" : "Mostrar 2aI"
|
|
120
|
+
}]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"code" : "code2aII",
|
|
124
|
+
"display" : "Anzeige 2aII",
|
|
125
|
+
"definition" : "Mein zweiter Third-Level-Code",
|
|
126
|
+
"definition:en" : "My second third level code",
|
|
127
|
+
"_definition" : {
|
|
128
|
+
"extension" : [{
|
|
129
|
+
"url" : "http://hl7.org/fhir/StructureDefinition/translation",
|
|
130
|
+
"extension" : [{
|
|
131
|
+
"url" : "lang",
|
|
132
|
+
"valueCode" : "en"
|
|
133
|
+
},{
|
|
134
|
+
"url" : "content",
|
|
135
|
+
"valueString" : "My second third level code"
|
|
136
|
+
}]
|
|
137
|
+
}]
|
|
138
|
+
}
|
|
139
|
+
}]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"code" : "code2b",
|
|
143
|
+
"display" : "Anzeige 2b",
|
|
144
|
+
"definition" : "Mein zweiter Second-Level-Code",
|
|
145
|
+
"definition:en" : "My second second level code",
|
|
146
|
+
"_definition" : {
|
|
147
|
+
"extension" : [{
|
|
148
|
+
"url" : "http://hl7.org/fhir/StructureDefinition/translation",
|
|
149
|
+
"extension" : [{
|
|
150
|
+
"url" : "lang",
|
|
151
|
+
"valueCode" : "en"
|
|
152
|
+
},{
|
|
153
|
+
"url" : "content",
|
|
154
|
+
"valueString" : "My second second level code"
|
|
155
|
+
}]
|
|
156
|
+
}]
|
|
157
|
+
},
|
|
158
|
+
"designation" : [{
|
|
159
|
+
"language" : "en",
|
|
160
|
+
"value" : "Display 2b"
|
|
161
|
+
}]
|
|
162
|
+
}]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"code" : "code3",
|
|
166
|
+
"display" : "Anzeige 3",
|
|
167
|
+
"definition" : "Mein Third-Level-Code",
|
|
168
|
+
"definition:en" : "My Third Level Code",
|
|
169
|
+
"_definition" : {
|
|
170
|
+
"extension" : [{
|
|
171
|
+
"url" : "http://hl7.org/fhir/StructureDefinition/translation",
|
|
172
|
+
"extension" : [{
|
|
173
|
+
"url" : "lang",
|
|
174
|
+
"valueCode" : "en"
|
|
175
|
+
},{
|
|
176
|
+
"url" : "content",
|
|
177
|
+
"valueString" : "My Third Level Code"
|
|
178
|
+
}]
|
|
179
|
+
}]
|
|
180
|
+
},
|
|
181
|
+
"designation" : [{
|
|
182
|
+
"language" : "en",
|
|
183
|
+
"value" : "Display 3"
|
|
184
|
+
}]
|
|
185
|
+
}]
|
|
186
|
+
}
|