fhirsmith 0.5.6 → 0.6.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 +18 -0
- package/library/html-server.js +2 -1
- package/library/package-manager.js +37 -32
- package/library/utilities.js +10 -1
- package/package.json +1 -1
- package/packages/package-crawler.js +103 -46
- package/packages/packages.js +14 -7
- package/publisher/publisher.js +15 -3
- package/registry/api.js +173 -191
- package/registry/crawler.js +72 -58
- package/registry/model.js +14 -8
- package/registry/registry.js +2 -0
- package/root-template.html +1 -0
- package/server.js +109 -45
- package/tx/cs/cs-api.js +18 -1
- package/tx/cs/cs-base.js +1 -0
- package/tx/cs/cs-rxnorm.js +9 -2
- package/tx/cs/cs-snomed.js +17 -2
- package/tx/html/codesystem-operations.liquid +17 -24
- package/tx/html/valueset-operations.liquid +46 -52
- package/tx/library/codesystem.js +6 -1
- package/tx/library/renderer.js +81 -7
- package/tx/library.js +18 -3
- package/tx/provider.js +4 -2
- package/tx/sct/expressions.js +20 -9
- package/tx/tx-html.js +143 -50
- package/tx/tx.js +2 -2
- package/tx/workers/expand.js +61 -9
- package/tx/workers/search.js +5 -2
- package/tx/xversion/xv-terminologyCapabilities.js +1 -1
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
<div class="
|
|
2
|
-
<
|
|
3
|
-
<
|
|
1
|
+
<div class="operation-form" style="margin-bottom: 15px;">
|
|
2
|
+
<strong>Lookup</strong>
|
|
3
|
+
<form method="get" action="$lookup" style="display: inline-block; margin-left: 10px;">
|
|
4
|
+
<input type="hidden" name="system" value="{{ url }}"/>
|
|
5
|
+
<input type="text" name="code" placeholder="Code" required size="20"/>
|
|
6
|
+
<button type="submit" class="btn btn-sm btn-primary">Lookup</button>
|
|
7
|
+
</form>
|
|
8
|
+
</div>
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<div class="operation-form" style="margin-bottom: 15px;">
|
|
15
|
-
<strong>Subsumes</strong>
|
|
16
|
-
<form method="get" action="$subsumes" style="display: inline-block; margin-left: 10px;">
|
|
17
|
-
<input type="hidden" name="system" value="{{ url }}"/>
|
|
18
|
-
<input type="text" name="codeA" placeholder="Code A" required size="15"/>
|
|
19
|
-
<input type="text" name="codeB" placeholder="Code B" required size="15"/>
|
|
20
|
-
<button type="submit" class="btn btn-sm btn-primary">Subsumes</button>
|
|
21
|
-
</form>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
10
|
+
<div class="operation-form" style="margin-bottom: 15px;">
|
|
11
|
+
<strong>Subsumes</strong>
|
|
12
|
+
<form method="get" action="$subsumes" style="display: inline-block; margin-left: 10px;">
|
|
13
|
+
<input type="hidden" name="system" value="{{ url }}"/>
|
|
14
|
+
<input type="text" name="codeA" placeholder="Code A" required size="15"/>
|
|
15
|
+
<input type="text" name="codeB" placeholder="Code B" required size="15"/>
|
|
16
|
+
<button type="submit" class="btn btn-sm btn-primary">Subsumes</button>
|
|
17
|
+
</form>
|
|
18
|
+
</div>
|
|
@@ -1,54 +1,48 @@
|
|
|
1
|
-
<div class="operations" style="margin-top: 20px;">
|
|
2
|
-
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="toggleOperations('{{ opsId }}')">Show Operations</button>
|
|
3
|
-
<div id="{{ opsId }}" style="display: none; margin-top: 10px;">
|
|
4
1
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
2
|
+
<div class="operation-form" style="margin-bottom: 15px;">
|
|
3
|
+
<strong>Expand</strong>
|
|
4
|
+
<form method="get" action="$expand" style="margin-left: 10px; margin-top: 5px;">
|
|
5
|
+
<input type="hidden" name="url" value="{{ url }}"/>
|
|
6
|
+
<table class="grid" cellpadding="0" cellspacing="0">
|
|
7
|
+
<tr>
|
|
8
|
+
<td>Filter: <input type="text" name="filter" size="20"/></td>
|
|
9
|
+
<td>Language: <input type="text" name="displayLanguage" size="10"/></td>
|
|
10
|
+
</tr>
|
|
11
|
+
<tr>
|
|
12
|
+
<td colspan="2">
|
|
13
|
+
<input type="checkbox" name="includeDesignations" id="expand_desig" value="true"/>
|
|
14
|
+
<label for="expand_desig">Include Designations</label>
|
|
15
|
+
<input type="checkbox" name="activeOnly" id="expand_active" value="true"/>
|
|
16
|
+
<label for="expand_active">Active Only</label>
|
|
17
|
+
</td>
|
|
18
|
+
</tr>
|
|
19
|
+
</table>
|
|
20
|
+
<button type="submit" class="btn btn-sm btn-primary">Expand</button>
|
|
21
|
+
</form>
|
|
22
|
+
</div>
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
24
|
+
<div class="operation-form" style="margin-bottom: 15px;">
|
|
25
|
+
<strong>Validate Code (ValueSet)</strong>
|
|
26
|
+
<form method="get" action="$validate-code" style="margin-left: 10px; margin-top: 5px;">
|
|
27
|
+
<input type="hidden" name="url" value="{{ url }}"/>
|
|
28
|
+
<input type="hidden" name="inferSystem" id="{{ inferSystemId }}" value="true"/>
|
|
29
|
+
<table class="grid" cellpadding="0" cellspacing="0">
|
|
30
|
+
<tr>
|
|
31
|
+
<td>System: <input type="text" name="system" id="{{ vcSystemId }}" size="30" onchange="updateInferSystem('{{ vcSystemId }}', '{{ inferSystemId }}')"/></td>
|
|
32
|
+
<td>Version: <input type="text" name="version" size="10"/></td>
|
|
33
|
+
</tr>
|
|
34
|
+
<tr>
|
|
35
|
+
<td>Code: <input type="text" name="code" size="20" required/></td>
|
|
36
|
+
<td>Display: <input type="text" name="display" size="20"/></td>
|
|
37
|
+
</tr>
|
|
38
|
+
<tr>
|
|
39
|
+
<td>Language: <input type="text" name="displayLanguage" size="10"/></td>
|
|
40
|
+
<td>
|
|
41
|
+
<input type="checkbox" name="abstract" value="true"/>
|
|
42
|
+
<label>Abstract</label>
|
|
43
|
+
</td>
|
|
44
|
+
</tr>
|
|
45
|
+
</table>
|
|
46
|
+
<button type="submit" class="btn btn-sm btn-primary">Validate Code</button>
|
|
47
|
+
</form>
|
|
48
|
+
</div>
|
package/tx/library/codesystem.js
CHANGED
|
@@ -33,7 +33,12 @@ class CodeSystem extends CanonicalResource {
|
|
|
33
33
|
// Convert to R5 format internally (modifies input for performance)
|
|
34
34
|
this.jsonObj = codeSystemToR5(this.jsonObj, fhirVersion);
|
|
35
35
|
if (!noMaps) {
|
|
36
|
-
|
|
36
|
+
try {
|
|
37
|
+
this.validate();
|
|
38
|
+
} catch (e) {
|
|
39
|
+
const id = this.jsonObj?.url ? `${this.jsonObj.url}|${this.jsonObj.version || ''}` : this.jsonObj?.name || 'unknown';
|
|
40
|
+
throw new Error(`${e.message} (in ${id})`);
|
|
41
|
+
}
|
|
37
42
|
this.buildMaps();
|
|
38
43
|
}
|
|
39
44
|
}
|
package/tx/library/renderer.js
CHANGED
|
@@ -147,6 +147,7 @@ class Renderer {
|
|
|
147
147
|
this.renderProperty(tbl, 'GENERAL_TITLE', res.title);
|
|
148
148
|
this.renderProperty(tbl, 'GENERAL_STATUS', res.status);
|
|
149
149
|
this.renderPropertyMD(tbl, 'GENERAL_DEFINITION', res.description);
|
|
150
|
+
this.renderPropertyMD(tbl, 'GENERAL_PURPOSE', res.purpose);
|
|
150
151
|
this.renderProperty(tbl, 'CANON_REND_PUBLISHER', res.publisher);
|
|
151
152
|
this.renderProperty(tbl, 'CANON_REND_COMMITTEE', Extensions.readString(res, 'http://hl7.org/fhir/StructureDefinition/structuredefinition-wg'));
|
|
152
153
|
this.renderProperty(tbl, 'GENERAL_COPYRIGHT', res.copyright);
|
|
@@ -339,7 +340,7 @@ class Renderer {
|
|
|
339
340
|
}
|
|
340
341
|
if (vs.expansion) {
|
|
341
342
|
div_.h2().tx("Expansion");
|
|
342
|
-
await this.renderExpansion(div_
|
|
343
|
+
await this.renderExpansion(div_, vs, tbl);
|
|
343
344
|
}
|
|
344
345
|
|
|
345
346
|
return div_.toString();
|
|
@@ -419,7 +420,7 @@ class Renderer {
|
|
|
419
420
|
li.tx(this.translate('VALUE_SET_ALL_CODES_DEF')+" ");
|
|
420
421
|
await this.renderLink(li,inc.system+(inc.version ? "|"+inc.version : ""));
|
|
421
422
|
} else if (inc.concept) {
|
|
422
|
-
li.tx(this.translate('VALUE_SET_THESE_CODES_DEF'));
|
|
423
|
+
li.tx(this.translate('VALUE_SET_THESE_CODES_DEF')+" ");
|
|
423
424
|
await this.renderLink(li,inc.system+(inc.version ? "|"+inc.version : ""));
|
|
424
425
|
li.tx(":");
|
|
425
426
|
const ul = li.ul();
|
|
@@ -898,18 +899,45 @@ class Renderer {
|
|
|
898
899
|
return count;
|
|
899
900
|
}
|
|
900
901
|
|
|
902
|
+
async renderVSExpansion(vs, showProps) {
|
|
903
|
+
let div_ = div();
|
|
904
|
+
let tbl;
|
|
905
|
+
if (showProps) {
|
|
906
|
+
div_.h2().tx("Expansion Properties");
|
|
907
|
+
tbl = div_.table("grid");
|
|
908
|
+
} else {
|
|
909
|
+
tbl = div(); // dummy
|
|
910
|
+
}
|
|
911
|
+
await this.renderExpansion(div_.table("grid"), vs, tbl);
|
|
912
|
+
return div_.toString();
|
|
913
|
+
}
|
|
914
|
+
|
|
901
915
|
async renderExpansion(x, vs, tbl) {
|
|
902
916
|
this.renderProperty(tbl, 'Expansion Identifier', vs.expansion.identifier);
|
|
903
917
|
this.renderProperty(tbl, 'Expansion Timestamp', vs.expansion.timestamp);
|
|
904
918
|
this.renderProperty(tbl, 'Expansion Total', vs.expansion.total);
|
|
905
919
|
this.renderProperty(tbl, 'Expansion Offset', vs.expansion.offset);
|
|
920
|
+
const warnings = [];
|
|
921
|
+
const warningNames = new Set(['deprecated', 'withdrawn', 'retired', 'experimental', 'draft']);
|
|
922
|
+
const useds = [];
|
|
923
|
+
const usedNames = new Set(['codesystem', 'valueset', 'supplement']);
|
|
906
924
|
for (let p of vs.expansion.parameter || []) {
|
|
907
|
-
if
|
|
925
|
+
if (p.name.startsWith('warning-') && warningNames.has(p.name.substring(8))) {
|
|
926
|
+
warnings.push(p);
|
|
927
|
+
} else if (p.name.startsWith('used-') && usedNames.has(p.name.substring(5))) {
|
|
928
|
+
useds.push(p);
|
|
929
|
+
} else if( getValueName(p) === 'valueUri' || getValueName(p) === 'valueCanonical') {
|
|
908
930
|
await this.renderPropertyLink(tbl, "Parameter: " + p.name, getValuePrimitive(p));
|
|
909
931
|
} else {
|
|
910
932
|
this.renderProperty(tbl, "Parameter: " + p.name, getValuePrimitive(p));
|
|
911
933
|
}
|
|
912
934
|
}
|
|
935
|
+
if (useds.length > 0) {
|
|
936
|
+
await this.renderUsed(x, useds);
|
|
937
|
+
}
|
|
938
|
+
if (warnings.length > 0) {
|
|
939
|
+
await this.renderWarnings(x, warnings);
|
|
940
|
+
}
|
|
913
941
|
|
|
914
942
|
if (!vs.expansion.contains || vs.expansion.contains.length === 0) {
|
|
915
943
|
x.para().i().tx('No concepts in expansion');
|
|
@@ -935,10 +963,10 @@ class Renderer {
|
|
|
935
963
|
}
|
|
936
964
|
headerRow.th().tx(this.translate('TX_DISPLAY'));
|
|
937
965
|
if (columnInfo.hasAbstract) {
|
|
938
|
-
headerRow.th().tx('Abstract');
|
|
966
|
+
headerRow.th().tx(this.translate('Abstract'));
|
|
939
967
|
}
|
|
940
968
|
if (columnInfo.hasInactive) {
|
|
941
|
-
headerRow.th().tx('
|
|
969
|
+
headerRow.th().tx(this.translate('VALUE_SET_INACTIVE'));
|
|
942
970
|
}
|
|
943
971
|
|
|
944
972
|
// Property columns (from expansion.property definitions)
|
|
@@ -1117,12 +1145,12 @@ class Renderer {
|
|
|
1117
1145
|
|
|
1118
1146
|
// Abstract column
|
|
1119
1147
|
if (columnInfo.hasAbstract) {
|
|
1120
|
-
tr.td().tx(contains.abstract === true ? '
|
|
1148
|
+
tr.td().tx(contains.abstract === true ? 'abstract' : '');
|
|
1121
1149
|
}
|
|
1122
1150
|
|
|
1123
1151
|
// Inactive column
|
|
1124
1152
|
if (columnInfo.hasInactive) {
|
|
1125
|
-
tr.td().tx(contains.inactive === true ? '
|
|
1153
|
+
tr.td().tx(contains.inactive === true ? this.translate('VALUE_SET_INACT') : '');
|
|
1126
1154
|
}
|
|
1127
1155
|
|
|
1128
1156
|
// Property columns
|
|
@@ -1566,6 +1594,52 @@ class Renderer {
|
|
|
1566
1594
|
|
|
1567
1595
|
return div_.toString();
|
|
1568
1596
|
}
|
|
1597
|
+
|
|
1598
|
+
async renderWarnings(x, warnings) {
|
|
1599
|
+
await this.renderWarningsForStatus(x, 'deprecated', warnings);
|
|
1600
|
+
await this.renderWarningsForStatus(x, 'withdrawn', warnings);
|
|
1601
|
+
await this.renderWarningsForStatus(x, 'retired', warnings);
|
|
1602
|
+
await this.renderWarningsForStatus(x, 'experimental', warnings);
|
|
1603
|
+
await this.renderWarningsForStatus(x, 'draft', warnings);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
async renderWarningsForStatus(x, name, warnings) {
|
|
1607
|
+
const wl = warnings.filter(item => item.name == 'warning-'+name);
|
|
1608
|
+
if (wl && wl.length > 0) {
|
|
1609
|
+
x.para().tx(`This ValueSet depends on the following ${name} ValueSets: `);
|
|
1610
|
+
let ul = x.ul();
|
|
1611
|
+
for (const w of wl) {
|
|
1612
|
+
const linkinfo = await this.linkResolver.resolveURL(this.opContext, getValuePrimitive(w));
|
|
1613
|
+
if (linkinfo) {
|
|
1614
|
+
ul.li().ah(linkinfo.link).tx(linkinfo.description);
|
|
1615
|
+
} else {
|
|
1616
|
+
ul.li().code().tx(getValuePrimitive(w));
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
async renderUsed(x, list) {
|
|
1623
|
+
x.para().tx(`This ValueSet depends on the following items:`);
|
|
1624
|
+
let ul = x.ul();
|
|
1625
|
+
await this.renderUsedForType(ul, 'codesystem', 'CodeSystem', list);
|
|
1626
|
+
await this.renderUsedForType(ul, 'valueset', 'ValueSet', list);
|
|
1627
|
+
await this.renderUsedForType(ul, 'supplement', 'Supplement', list);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
async renderUsedForType(ul, name, title, list) {
|
|
1631
|
+
const wl = list.filter(item => item.name == 'used-' + name);
|
|
1632
|
+
for (const w of wl) {
|
|
1633
|
+
const li = ul.li();
|
|
1634
|
+
li.tx(title+": ");
|
|
1635
|
+
const linkinfo = await this.linkResolver.resolveURL(this.opContext, getValuePrimitive(w));
|
|
1636
|
+
if (linkinfo) {
|
|
1637
|
+
li.ah(linkinfo.link).tx(linkinfo.description);
|
|
1638
|
+
} else {
|
|
1639
|
+
li.code().tx(getValuePrimitive(w));
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1569
1643
|
}
|
|
1570
1644
|
|
|
1571
1645
|
module.exports = { Renderer };
|
package/tx/library.js
CHANGED
|
@@ -158,7 +158,12 @@ class Library {
|
|
|
158
158
|
this.log.info('Fetching Data from '+this.baseUrl);
|
|
159
159
|
|
|
160
160
|
for (const source of config.sources) {
|
|
161
|
-
|
|
161
|
+
try {
|
|
162
|
+
await this.processSource(source, this.packageManager, "fetch");
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error(`Failed to fetch source '${source}': ${error.message}`);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
162
167
|
}
|
|
163
168
|
|
|
164
169
|
this.log.info("Downloaded "+((this.totalDownloaded + this.packageManager.totalDownloaded)/ 1024)+" kB");
|
|
@@ -167,13 +172,23 @@ class Library {
|
|
|
167
172
|
this.#logSystemHeader();
|
|
168
173
|
|
|
169
174
|
for (const source of config.sources) {
|
|
170
|
-
|
|
175
|
+
try {
|
|
176
|
+
await this.processSource(source, this.packageManager, "cs");
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error(`Failed to load code systems from '${source}': ${error.message}`);
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
171
181
|
}
|
|
172
182
|
this.log.info('Loading Packages');
|
|
173
183
|
this.#logPackagesHeader();
|
|
174
184
|
|
|
175
185
|
for (const source of config.sources) {
|
|
176
|
-
|
|
186
|
+
try {
|
|
187
|
+
await this.processSource(source, this.packageManager, "npm");
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error(`Failed to load package '${source}': ${error.message}`);
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
177
192
|
}
|
|
178
193
|
|
|
179
194
|
const endMemory = process.memoryUsage();
|
package/tx/provider.js
CHANGED
|
@@ -153,6 +153,7 @@ class Provider {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
getCodeSystemById(opContext, id) {
|
|
156
|
+
|
|
156
157
|
// Search through codeSystems map for matching id
|
|
157
158
|
for (const cs of this.codeSystems.values()) {
|
|
158
159
|
if (opContext) opContext.deadCheck('getCodeSystemById');
|
|
@@ -318,9 +319,10 @@ class Provider {
|
|
|
318
319
|
factory = this.codeSystemFactories.get(vurlMM);
|
|
319
320
|
}
|
|
320
321
|
if (factory != null) {
|
|
322
|
+
let vdesc = version == null ? "" : factory.describeVersion(version);
|
|
321
323
|
return {
|
|
322
|
-
link: this.path+"/CodeSystem/"+factory.id(),
|
|
323
|
-
description: factory.
|
|
324
|
+
link: this.path+"/CodeSystem/x-"+factory.id(),
|
|
325
|
+
description: factory.nameBase()+' '+vdesc
|
|
324
326
|
};
|
|
325
327
|
}
|
|
326
328
|
let cs = this.codeSystems.get(vurl);
|
package/tx/sct/expressions.js
CHANGED
|
@@ -1469,7 +1469,7 @@ class SnomedExpressionServices {
|
|
|
1469
1469
|
/**
|
|
1470
1470
|
* Validate concept reference
|
|
1471
1471
|
*/
|
|
1472
|
-
checkConcept(concept,
|
|
1472
|
+
checkConcept(concept, limits) {
|
|
1473
1473
|
if (concept.code) {
|
|
1474
1474
|
const conceptId = BigInt(concept.code);
|
|
1475
1475
|
const result = this.concepts.findConcept(conceptId);
|
|
@@ -1480,13 +1480,24 @@ class SnomedExpressionServices {
|
|
|
1480
1480
|
throw new Error(`Concept ${concept.code} not found`);
|
|
1481
1481
|
}
|
|
1482
1482
|
}
|
|
1483
|
-
if (
|
|
1484
|
-
// if a limit is specified, then the concept has to be a specialization of
|
|
1485
|
-
let
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1483
|
+
if (limits && concept.reference) {
|
|
1484
|
+
// if a limit is specified, then the concept has to be a specialization of one of them.
|
|
1485
|
+
let ok = false;
|
|
1486
|
+
for (const limit of limits) {
|
|
1487
|
+
let parentRef = this.concepts.findConcept(limit);
|
|
1488
|
+
let descendentsRef = this.concepts.getAllDesc(parentRef.index);
|
|
1489
|
+
const descendants = this.refs.getReferences(descendentsRef);
|
|
1490
|
+
if (descendants && descendants.includes(concept.reference)) {
|
|
1491
|
+
ok = true;
|
|
1492
|
+
break;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (!ok) {
|
|
1496
|
+
if (limits.length == 1) {
|
|
1497
|
+
throw new Error(`Concept ${concept.code} is not valid in this context (must be a ${limits[0]})`);
|
|
1498
|
+
} else {
|
|
1499
|
+
throw new Error(`Concept ${concept.code} is not valid in this context (must be a descendent of one of ${limits})`);
|
|
1500
|
+
}
|
|
1490
1501
|
}
|
|
1491
1502
|
}
|
|
1492
1503
|
|
|
@@ -1615,7 +1626,7 @@ class SnomedExpressionServices {
|
|
|
1615
1626
|
* Validate refinement
|
|
1616
1627
|
*/
|
|
1617
1628
|
checkRefinement(refinement) {
|
|
1618
|
-
this.checkConcept(refinement.name, '410662002');
|
|
1629
|
+
this.checkConcept(refinement.name, ['410662002', '106237007']);
|
|
1619
1630
|
this.checkExpression(refinement.value);
|
|
1620
1631
|
}
|
|
1621
1632
|
|