diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c11fbeb..04b67ce4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,36 @@ All notable changes to Health Intersections FHIRsmith will be documented in this The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.18.1] - 2026-10-25 + + +### Security + +- + +### Added + +- ICF (`http://hl7.org/fhir/sid/icf`): qualified codes. A category may be followed by `.` and one qualifier digit per position, the positions depending on the component: extent for body functions (`b`); extent, nature and localization for body structures (`s`); performance, capacity, and optionally capacity with assistance and performance without assistance for activities and participation (`d`); one for environmental factors (`e`), a barrier after `.` or a facilitator after `+` in place of the `.`. Each digit is checked against its scale in the code system (`q1`-`q6`, added to the ICF code systems in fhir.tx.support with a `kind` property). A qualified code gets a generated display (8, not specified, is left out of it; `d` qualifiers are labelled `performance`, `capacity`, ...), its qualifiers as properties in `$lookup` (`extent`, `nature`, `localization`, `performance`, `capacity`, `capacity-with-assistance`, `performance-without-assistance`, `barrier`, `facilitator`), and sits under its category for subsumption and `is-a` filters. Value sets can control qualifiers with an `exists` `true`/`false` filter on any of those properties (8 counts as absent) and `qualified` `=` `false`. ICF is now marked as not closed. The behaviour is in `tx/cs/cs-icf.js`, a subclass of the CodeSystem provider that `FhirCodeSystemProvider.create()` picks by url - the way to give any code system loaded from a package behaviour of its own +- SNOMED CT alternate identifiers (`sct2_Identifier`, e.g. the LOINC Extension's LOINC codes): the importer reads them into the cache (cache format 18, written only when there are identifiers, so other caches stay 17). `$lookup` returns them as Coding properties named for the scheme (`loinc` = `http://loinc.org#...`), by default and when asked for. `$expand`: the filter `mapped:` `exists` `true`/`false` (e.g. `mapped:30051010000102`) for concepts that do or don't have one. `$translate`: each module with identifiers defines an implicit map `/version/?fhir_cm=` (e.g. `?fhir_cm=11010000107`), `equivalent` in both directions, nominated by url or found from the systems (`targetSystem` `http://loinc.org` from a SNOMED CT coding, or `http://snomed.info/sct|` from a LOINC one) +- `$lookup` on SNOMED CT: when the request names properties, only those are returned - effectiveTime, module, parent, child and the defining relationships are no longer added regardless +- Implicit ConceptMaps nominated by url are looked for in every version of a code system, not just the first loaded (a SNOMED CT edition's maps are named by its edition URI) +- SNOMED CT test edition: a subset of the LOINC Extension (LOINC ontology, module `11010000107`) - twelve LOINC-coded concepts with their LOINC identifiers (`sct2_Identifier`), plus the module and identifier scheme concepts - generated from the LOINC Extension release by `utilities/make-sct-loinc-subset.js`. The test cache is now `sct_test_20250909d.cache` (2280 concepts) +- LOINC importer: mappings from LOINC codes, parts and answers to other code systems, in two new tables - `ExternalCodes` (system, version, code, display; one row per target) and `ExternalMappings` (LOINC code to target, with an equivalence). Read from `PartFile/PartRelatedCodeMapping.csv` (the file's own equivalence; the RadLex rows are left out for now), `LoincIeeeMedicalDeviceCodeMappingTable` (MDC, `urn:iso:std:iso:11073:10101`, the CF_CODE10 as the code and the REFID as display; IEEE's `broader`/`narrower` are from the LOINC side, so they become `narrower`/`wider`) and the `ExtCode*` columns of `AnswerList.csv` (`equivalent`; LOINC's `CMS/HCPCS` and `https://www.cdc.gov/phin` systems become `http://www.cms.gov/Medicare/Coding/HCPCSReleaseCodeSets` and `urn:oid:2.16.840.1.113883.6.238`). Both mapping files are optional. LOINC caches need rebuilding to get them; nothing uses them yet +- LOINC importer (and subsetter): CSV files are read by record, not by line, so a quoted field with a line break in it is one record. LOINC 2.83's `ComponentHierarchyBySystem.csv` has one (the text of `LP409416-7`), and the import failed with `UNIQUE constraint failed: Config.ConfigKey` - the second half of the record looked like a second root +- LOINC: the mappings to other code systems are used. `$lookup` returns them as Coding properties named for the map (`sct`, `chebi`, `mdc`, `race`...), with the equivalence as a subproperty, by default and when asked for. `$translate`: each target system has an implicit map `http://loinc.org/cm/` - `loinc`, `ncbi` (PubChem), `sct`, `hcpcs`, `gene-names`, `rxnorm`, `chebi`, `ncbi-clinvar`, `ncbi-gene`, `ncbi-taxo`, `mdc`, `race` - that works in both directions (LOINC to the system, and the system to LOINC), found from the target system or nominated by url. Found from the systems, a map is only offered for a code it has a mapping for, so translations that a SNOMED CT edition answers (the LOINC Extension) are unchanged. Targets in other systems (`CMS LCDS`, `Unicode`) are not used yet. Code system providers' `findImplicitConceptMaps` now also gets the code being translated + +### Changed + +- + +### Fixed + +- + +### Tx Conformance Statement + +(paste) + ## [0.18.0] - 2026-10-23 A big release: a new IPS module (prototype), a secured and documented registry with uptime reporting, a syndication feed for the terminology server, a daily email report, ICF qualified codes, SNOMED CT edition to edition translation and much more ECL, and the ICD-11 representation agreed at the October 2026 test-case review meetings. Plus the outcome of security reviews of the registry, IPS and token modules. The README now says what state each module is in. @@ -43,7 +73,6 @@ Note: We are heading towards a 1.0.0 release. - Home page and dashboard: the uptime box now also reports the server's overall uptime - `Uptime: 38m 16s (99.3% overall)` - the proportion of the time since the first recorded run that the server has been running. Each run is recorded in the statistics database (`server_runs` in `stats.db`) with a heartbeat every minute, so a crash loses at most a minute. Not shown when statistics are turned off - `library/fhir-search-values.js`: the date range, prefix and comma-splitting parts of FHIR search, moved out of the testing module for sharing - `library/package-manager.js`: a cached package folder with no `package/package.json` (a download that failed after the folder was made) is fetched again instead of being used -- ICF (`http://hl7.org/fhir/sid/icf`): qualified codes. A category may be followed by `.` and one qualifier digit per position, the positions depending on the component: extent for body functions (`b`); extent, nature and localization for body structures (`s`); performance, capacity, and optionally capacity with assistance and performance without assistance for activities and participation (`d`). Qualifiers on environmental factors (`e`) are not supported yet. Each digit is checked against its scale in the code system (`q1`-`q4`, added to the ICF code systems in fhir.tx.support with a `kind` property). A qualified code gets a generated display (8, not specified, is left out of it; `d` qualifiers are labelled `performance`, `capacity`, ...), its qualifiers as properties in `$lookup` (`extent`, `nature`, `localization`, `performance`, `capacity`, `capacity-with-assistance`, `performance-without-assistance`), and sits under its category for subsumption and `is-a` filters. Value sets can control qualifiers with an `exists` `true`/`false` filter on any of those properties (8 counts as absent) and `qualified` `=` `false`. ICF is now marked as not closed. The behaviour is in `tx/cs/cs-icf.js`, a subclass of the CodeSystem provider that `FhirCodeSystemProvider.create()` picks by url - the way to give any code system loaded from a package behaviour of its own - `$translate`: SNOMED CT edition to edition translation with no map - from an edition to one it depends on (per the module dependency reference set), using the implicit map `/version/?fhir_cm=edition/` (the url agreed with the SNOMED committee), found from `targetSystem` or nominated by url. A concept in the target edition's modules is its own translation; an inactive concept translates through its historical associations; a fully defined extension concept translates to its normal form (or the target concept with that normal form), when that uses only target edition content; anything else translates to its proximal target-edition ancestors as `source-is-narrower-than-target`, unless those are only top-level hierarchies, which is reported as the reason there is no translation. The source concept in a match carries its version, as given - `$translate`: when the target code a map directs to is not in its code system's normal form, the match has a `normalized-concept`. A source code that has a normal form is looked up both as given and in its normal form (a map entry written in the normal form matches any code with that normal form; the map's own entries are not normalised). A target in a code system the server doesn't know gets a `note` saying it was not validated, and a target code that is not valid gives an `error` - `$lookup`: a warning when the `displayLanguage` asked for has no display for the code, and the display is in the default language (`LOOKUP_DISPLAY_LANGUAGE_FALLBACK`) diff --git a/library/daily-report.js b/library/daily-report.js index 5c2d0f41..8ee441a8 100644 --- a/library/daily-report.js +++ b/library/daily-report.js @@ -85,6 +85,15 @@ function formatSpan(ms) { return parts.join(' '); } +/** "24 hours", "3 days" - for the report title; rounded, since it's a description of the period */ +function describeSpan(ms) { + const hours = Math.round(ms / 3600000); + if (hours < 48) { + return plural(Math.max(1, hours), 'hour'); + } + return plural(Math.round(hours / 24), 'day'); +} + function plural(n, word) { return `${n} ${word}${n === 1 ? '' : 's'}`; } @@ -243,8 +252,7 @@ class DailyReport { */ async build(since, now = Date.now()) { const parts = []; - parts.push(`# ${this.serverId} daily report`); - parts.push(`${localDateTime(now)}, covering the ${formatSpan(now - since)} since ${localDateTime(since)}.`); + parts.push(`# ${this.serverId} daily report for ${localDateTime(now)} covering the last ${describeSpan(now - since)}`); parts.push('## Stability'); parts.push(this.stabilityParagraph(since, now)); for (const [name, module] of Object.entries(this.modules)) { @@ -347,25 +355,26 @@ class DailyReport { * the disk they're on. */ systemParagraph(now) { - const sentences = []; - sentences.push(`${this.serverId} is running FHIRsmith v${this.version} on Node.js ${process.version} (${os.type()} ${os.release()}, ${os.arch()}).`); + // one line each; the two trailing spaces are markdown's line break + const lines = []; + lines.push(`Versions: FHIRsmith = v${this.version}. Node = ${process.version} (${os.type()} ${os.release()}, ${os.arch()})`); if (this.stats && typeof this.stats.startTime === 'number') { - let up = `This run has been up for ${formatSpan(now - this.stats.startTime)}, since ${localDateTime(this.stats.startTime)}`; + let up = `Uptime: This run = ${formatSpan(now - this.stats.startTime)}`; const overall = typeof this.stats.overallUptime === 'function' ? this.stats.overallUptime(now) : null; if (overall) { const pct = overall.percent >= 100 ? '100' : (Math.floor(overall.percent * 10) / 10).toFixed(1); - up += `; over all its recorded runs (since ${localDate(overall.since)}) the server has been up ${pct}% of the time`; + up += `. All runs, ${pct}%`; } - sentences.push(up + '.'); + lines.push(up); } - sentences.push(this.logsSentence()); + lines.push(`Space: ${this.logsText()}`); if (this.npmAudit && typeof this.npmAudit.reportText === 'function') { const audit = this.npmAudit.reportText(); if (audit) { - sentences.push(audit); + lines.push(`Audit: ${audit}`); } } - return sentences.join(' '); + return lines.join(' \n'); } /** @@ -388,7 +397,7 @@ class DailyReport { return `${plural(failed.length, 'background task')} ${failed.length === 1 ? 'is' : 'are'} in a failed state:\n\n${lines.join('\n')}`; } - logsSentence() { + logsText() { const logDir = folders.logsDir(); try { const files = fs.readdirSync(logDir).filter(f => f.endsWith('.log')); @@ -402,13 +411,13 @@ class DailyReport { const free = fstats.bavail * fstats.bsize; const size = fstats.blocks * fstats.bsize; const freePct = size > 0 ? Math.round((free * 100) / size) : 0; - disk = `, on a disk with ${formatBytes(free)} free of ${formatBytes(size)} (${freePct}% free)`; + disk = ` on a disk with ${formatBytes(free)} free of ${formatBytes(size)} (${freePct}% free)`; } catch { - disk = ', on a disk whose free space could not be read'; + disk = ' on a disk whose free space could not be read'; } - return `The logs folder (${logDir}) holds ${plural(files.length, 'log file')} totalling ${formatBytes(total)}${disk}.`; + return `${plural(files.length, 'log file')} totalling ${formatBytes(total)}${disk}.`; } catch (e) { - return `The logs folder (${logDir}) could not be read: ${e.message}.`; + return `the logs folder (${logDir}) could not be read: ${e.message}.`; } } } @@ -417,6 +426,7 @@ module.exports = DailyReport; module.exports.nextMidnight = nextMidnight; module.exports.localDate = localDate; module.exports.formatSpan = formatSpan; +module.exports.describeSpan = describeSpan; // for modules writing their paragraph: "3 packages", "1 package" module.exports.plural = plural; module.exports.localDateTime = localDateTime; diff --git a/package-lock.json b/package-lock.json index eaf92615..5bf076af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fhirsmith", - "version": "0.18.0", + "version": "0.18.1-snapshot", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fhirsmith", - "version": "0.18.0", + "version": "0.18.1-snapshot", "license": "BSD-3", "dependencies": { "axios": "^1.13.4", @@ -3226,18 +3226,18 @@ } }, "node_modules/body-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.4.0.tgz", + "integrity": "sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^2.0.0", + "content-type": "^2.1.0", "debug": "^4.4.3", "http-errors": "^2.0.1", - "iconv-lite": "^0.7.2", + "iconv-lite": "^0.7.3", "on-finished": "^2.4.1", - "qs": "^6.15.2", + "qs": "^6.16.1", "raw-body": "^3.0.2", "type-is": "^2.1.0" }, @@ -3250,9 +3250,9 @@ } }, "node_modules/body-parser/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", "license": "MIT", "engines": { "node": ">=18" @@ -4676,9 +4676,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", - "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", "dev": true, "funding": [ { @@ -8181,9 +8181,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", - "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -9543,9 +9543,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.0.tgz", - "integrity": "sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==", + "version": "6.29.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz", + "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==", "license": "MIT", "optional": true, "engines": { diff --git a/package.json b/package.json index bc63e466..225ea54a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fhirsmith", - "version": "0.18.0", + "version": "0.18.1-snapshot", "txVersion": "1.9.6", "description": "A Node.js server that provides a collection of tools to serve the FHIR ecosystem", "main": "server.js", diff --git a/tests/cs/cs-icf.test.js b/tests/cs/cs-icf.test.js index 54f1f8e9..4c86d0fa 100644 --- a/tests/cs/cs-icf.test.js +++ b/tests/cs/cs-icf.test.js @@ -46,7 +46,7 @@ describe('ICF CodeSystem Provider', () => { expect(r.context).not.toBeInstanceOf(IcfQualifiedContext); }); - test.each(['b1100.2', 'b110.0', 'b1100.9', 's7300.183', 's7300.8', 'd450.4', 'd450.48', 'd450.1234'])('%s is a valid qualified code', async (code) => { + test.each(['b1100.2', 'b110.0', 'b1100.9', 's7300.183', 's7300.8', 'd450.4', 'd450.48', 'd450.1234', 'e310.2', 'e310.9', 'e310+3', 'e310+0', 'e310+8'])('%s is a valid qualified code', async (code) => { const r = await provider.locate(code); expect(r.message).toBeNull(); expect(r.context).toBeInstanceOf(IcfQualifiedContext); @@ -68,8 +68,11 @@ describe('ICF CodeSystem Provider', () => { ['d450.12345', 'takes 1 to 4 qualifier digits'], ['d450.5', "'5' is not a valid value for qualifier 1 (performance)"], ['d450.15', "'5' is not a valid value for qualifier 2 (capacity)"], - ['e310.2', 'qualifiers on environmental factors codes are not supported'], - ['e310+2', 'zzz-unknown'], + ['e310+9', "'9' is not a valid value for qualifier 1 (facilitator)"], + ['e310.23', "takes one qualifier digit after the '.' (barrier)"], + ['e310+23', "takes one qualifier digit after the '+' (facilitator)"], + ['b1100+2', "only an environmental factor (e) can be a facilitator"], + ['e9999+2', 'zzz-unknown'], ['b1.2', "'b1' is not one"], ['b110-b139.2', 'is not one'], ['q1-2.2', 'is not one'] @@ -92,6 +95,10 @@ describe('ICF CodeSystem Provider', () => { expect(await provider.display('d450.12')).toBe('Walking: performance MILD problem, capacity MODERATE problem'); expect(await provider.display('d450.8834')).toBe('Walking: capacity with assistance SEVERE problem, performance without assistance COMPLETE problem'); expect(await provider.display('d450.9')).toBe('Walking: performance not applicable'); + expect(await provider.display('e310.2')).toBe('Immediate family: MODERATE barrier'); + expect(await provider.display('e310+3')).toBe('Immediate family: SUBSTANTIAL facilitator'); + expect(await provider.display('e310.9')).toBe('Immediate family: not applicable'); + expect(await provider.display('e310+8')).toBe('Immediate family'); }); test('8 (not specified) is silent', async () => { @@ -128,6 +135,11 @@ describe('ICF CodeSystem Provider', () => { expect(value('extent')).toEqual([]); }); + test('an e code reports its barrier or facilitator', async () => { + expect((await provider.properties('e310.2')).filter(p => p.code === 'barrier').map(p => p.valueCode)).toEqual(['q5-2']); + expect((await provider.properties('e310+3')).filter(p => p.code === 'facilitator').map(p => p.valueCode)).toEqual(['q6-3']); + }); + test('extendLookup has the qualifiers and parent, but no children', async () => { const ctxt = (await provider.locate('b110.2')).context; const params = []; @@ -229,6 +241,12 @@ describe('ICF CodeSystem Provider', () => { expect(await member([['concept', 'is-a', 'd'], ['capacity', 'exists', 'true']], code)).toBe(inVs); }); + test.each([ + ['e310', false], ['e310.2', false], ['e310+3', true], ['e310+0', true], ['e310+8', false] + ])('facilitator exists true: %s -> %s', async (code, inVs) => { + expect(await member([['concept', 'is-a', 'e'], ['facilitator', 'exists', 'true']], code)).toBe(inVs); + }); + test('extent does not apply to d codes', async () => { expect(await member([['concept', 'is-a', 'd'], ['extent', 'exists', 'true']], 'd450.12')).toBe(false); expect(await member([['concept', 'is-a', 'd'], ['extent', 'exists', 'false']], 'd450.12')).toBe(true); diff --git a/tests/cs/cs-loinc.test.js b/tests/cs/cs-loinc.test.js index faf24892..7e3ca537 100644 --- a/tests/cs/cs-loinc.test.js +++ b/tests/cs/cs-loinc.test.js @@ -1,7 +1,7 @@ const sqlite3 = require('sqlite3').verbose(); const fs = require('fs'); const path = require('path'); -const { LoincDataMigrator } = require('../../tx/importers/import-loinc.module'); +const { LoincDataMigrator, normaliseExternalSystem } = require('../../tx/importers/import-loinc.module'); const { LoincServices, LoincServicesFactory, LoincProviderContext } = require('../../tx/cs/cs-loinc'); const { OperationContext } = require('../../tx/operation-context'); const {validateParameter} = require("../../library/utilities"); @@ -84,7 +84,9 @@ describe('LOINC Module Import', () => { { name: 'PropertyValues', minRecords: 1 }, // Property value lookups { name: 'Properties', minRecords: 1 }, // Code properties { name: 'Descriptions', minRecords: 1 }, // Code descriptions - { name: 'Closure', minRecords: 1 } // Hierarchical relationships + { name: 'Closure', minRecords: 1 }, // Hierarchical relationships + { name: 'ExternalCodes', minRecords: 1 }, // codes in other systems that LOINC maps to + { name: 'ExternalMappings', minRecords: 1 } ]; // Check each table @@ -200,6 +202,81 @@ describe('LOINC Module Import', () => { } }); + test('should read a hierarchy record whose text has a line break', async () => { + // tx/data/loinc's hierarchy has a CODE_TEXT with a line break in it, as LOINC 2.83 does. + // Read line by line, the second half looked like a second root, and the import failed + // on a duplicate Config key + const db = new sqlite3.Database(testDbPath); + try { + const parents = await new Promise((resolve, reject) => { + db.all(`SELECT p.Code FROM Relationships r + JOIN Codes c ON c.CodeKey = r.SourceKey + JOIN Codes p ON p.CodeKey = r.TargetKey + JOIN RelationshipTypes t ON t.RelationshipTypeKey = r.RelationshipTypeKey + WHERE c.Code = '107188-5' AND t.Description = 'parent'`, (err, rows) => err ? reject(err) : resolve(rows)); + }); + expect(parents.map(r => r.Code)).toEqual(['LP442038-8']); + expect(await getTableCountWhere(db, 'Config', 'ConfigKey = 3')).toBe(1); + } finally { + await closeDatabase(db); + } + }); + + test('should have external code mappings', async () => { + const db = new sqlite3.Database(testDbPath); + + const mapsFor = (code) => new Promise((resolve, reject) => { + db.all(`SELECT e.System, e.Version, e.Code, e.Display, m.Equivalence + FROM ExternalMappings m + JOIN Codes c ON c.CodeKey = m.CodeKey + JOIN ExternalCodes e ON e.ExternalCodeKey = m.ExternalCodeKey + WHERE c.Code = ? ORDER BY e.System, e.Code`, [code], (err, rows) => err ? reject(err) : resolve(rows)); + }); + + try { + // PartRelatedCodeMapping: the file's own equivalence is kept + const creatinine = await mapsFor('LP14355-9'); + expect(creatinine).toContainEqual({ + System: 'http://snomed.info/sct', Version: 'http://snomed.info/sct/900000000000207008/version/20260101', + Code: '15373003', Display: 'Creatinine (substance)', Equivalence: 'narrower' + }); + const bacteria = await mapsFor('LP14082-9'); + expect(bacteria).toContainEqual({ + System: 'https://www.ncbi.nlm.nih.gov/taxonomy', Version: null, Code: '2', Display: 'Bacteria', Equivalence: 'equivalent' + }); + + // IEEE: code is the CF_CODE10, display the REFID; the IEEE 'broader' (LOINC is broader) becomes 'narrower' + const potassium = await mapsFor('6298-4'); + expect(potassium).toEqual([{ + System: 'urn:iso:std:iso:11073:10101', Version: null, Code: '160016', Display: 'MDC_CONC_K_GEN', Equivalence: 'narrower' + }]); + + // AnswerList ExtCode*: always equivalent + const answer = await mapsFor('LA6576-8'); + expect(answer).toEqual([{ + System: 'http://snomed.info/sct', Version: 'http://snomed.info/sct/900000000000207008/version/20240701', + Code: '10828004', Display: expect.anything(), Equivalence: 'equivalent' + }]); + + // LOINC's names for some systems become their FHIR URIs + expect(normaliseExternalSystem('CMS/HCPCS')).toBe('http://www.cms.gov/Medicare/Coding/HCPCSReleaseCodeSets'); + expect(normaliseExternalSystem('https://www.cdc.gov/phin')).toBe('urn:oid:2.16.840.1.113883.6.238'); + expect(normaliseExternalSystem('CMS LCDS')).toBe('CMS LCDS'); + expect(normaliseExternalSystem('http://snomed.info/sct')).toBe('http://snomed.info/sct'); + + // the same target is stored once, however many codes map to it + const mdcTargets = await getTableCountWhere(db, 'ExternalCodes', `System = 'urn:iso:std:iso:11073:10101' AND Code = '160016'`); + expect(mdcTargets).toBe(1); + + // RadLex is not imported yet; every mapping points at real rows + expect(await getTableCountWhere(db, 'ExternalCodes', `System = 'http://www.radlex.org'`)).toBe(0); + expect(await getTableCountWhere(db, 'ExternalMappings', + 'CodeKey NOT IN (SELECT CodeKey FROM Codes) OR ExternalCodeKey NOT IN (SELECT ExternalCodeKey FROM ExternalCodes)')).toBe(0); + } finally { + await closeDatabase(db); + } + }); + test('should have reasonable data distribution', async () => { const db = new sqlite3.Database(testDbPath); @@ -240,6 +317,15 @@ function getTableCount(db, tableName) { }); } +function getTableCountWhere(db, tableName, where) { + return new Promise((resolve, reject) => { + db.get(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${where}`, (err, row) => { + if (err) reject(err); + else resolve(row.count); + }); + }); +} + function getConfigValue(db, configKey) { return new Promise((resolve, reject) => { db.get('SELECT Value FROM Config WHERE ConfigKey = ?', [configKey], (err, row) => { @@ -986,6 +1072,76 @@ describe('LOINC Provider', () => { }); }); + describe('External Mappings', () => { + const mappingProps = (params) => params + .filter(p => p.name === 'property') + .map(p => ({ + code: p.part.find(x => x.name === 'code').valueCode, + value: p.part.find(x => x.name === 'value').valueCoding, + equivalence: p.part.find(x => x.name === 'subproperty')?.part.find(x => x.name === 'value').valueCode + })) + .filter(p => p.value); + + test('lookup returns a part\'s mappings, named for the map, by default', async () => { + const params = []; + await provider.extendLookup('LP14355-9', [], params); + expect(mappingProps(params)).toEqual([ + { code: 'ncbi', value: { system: 'http://pubchem.ncbi.nlm.nih.gov', code: '588', display: 'Creatinine' }, equivalence: 'equivalent' }, + { code: 'sct', value: { system: 'http://snomed.info/sct', version: 'http://snomed.info/sct/900000000000207008/version/20260101', code: '15373003', display: 'Creatinine (substance)' }, equivalence: 'narrower' }, + { code: 'chebi', value: { system: 'https://www.ebi.ac.uk/chebi', code: 'CHEBI:16737', display: 'creatinine' }, equivalence: 'equivalent' } + ]); + }); + + test('lookup returns only the mappings asked for', async () => { + const params = []; + await provider.extendLookup('LP14355-9', ['sct'], params); + expect(mappingProps(params).map(p => p.code)).toEqual(['sct']); + }); + + test('lookup returns IEEE and answer list mappings', async () => { + let params = []; + await provider.extendLookup('6298-4', ['mdc'], params); + expect(mappingProps(params)).toEqual([ + { code: 'mdc', value: { system: 'urn:iso:std:iso:11073:10101', code: '160016', display: 'MDC_CONC_K_GEN' }, equivalence: 'narrower' } + ]); + params = []; + await provider.extendLookup('LA6576-8', ['sct'], params); + expect(mappingProps(params).map(p => p.value.code)).toEqual(['10828004']); + }); + + test('an implicit map is offered for a code it has a mapping for', async () => { + let maps = []; + await factory.findImplicitConceptMaps(maps, 'http://loinc.org', null, 'http://snomed.info/sct', 'LP14355-9'); + expect(maps.map(m => m.url)).toEqual(['http://loinc.org/cm/sct']); + + // the other way, the code is the SNOMED CT concept + maps = []; + await factory.findImplicitConceptMaps(maps, 'http://snomed.info/sct', null, 'http://loinc.org', '15373003'); + expect(maps.map(m => m.url)).toEqual(['http://loinc.org/cm/sct']); + + // no mapping for this code, or no map for this system + maps = []; + await factory.findImplicitConceptMaps(maps, 'http://loinc.org', null, 'http://snomed.info/sct', '6298-4'); + await factory.findImplicitConceptMaps(maps, 'http://loinc.org', null, 'http://hl7.org/fhir/sid/icd-10', 'LP14355-9'); + expect(maps).toEqual([]); + + expect((await factory.findImplicitConceptMap('http://loinc.org/cm/mdc')).url).toBe('http://loinc.org/cm/mdc'); + expect(await factory.findImplicitConceptMap('http://loinc.org/cm/unknown')).toBeNull(); + }); + + test('translations go both ways, with the relationship from the code translated', async () => { + const map = await factory.findImplicitConceptMap('http://loinc.org/cm/sct'); + const forward = await provider.getTranslations(map, { system: 'http://loinc.org', code: 'LP14355-9' }, 'http://snomed.info/sct', false); + expect(forward.map(t => [t.system, t.code, t.relationship])).toEqual([['http://snomed.info/sct', '15373003', 'source-is-broader-than-target']]); + + const back = await provider.getTranslations(map, { system: 'http://snomed.info/sct', code: '15373003' }, 'http://loinc.org', false); + expect(back.map(t => [t.system, t.code, t.display, t.relationship])).toEqual([['http://loinc.org', 'LP14355-9', 'Creatinine', 'source-is-narrower-than-target']]); + + // a code with nothing in this map + expect(await provider.getTranslations(map, { system: 'http://loinc.org', code: '6298-4' }, 'http://snomed.info/sct', false)).toEqual([]); + }); + }); + describe('Iterator Support', () => { test('should iterate all codes', async () => { const iterator = await provider.iterator(null); diff --git a/tests/cs/csv-records.test.js b/tests/cs/csv-records.test.js new file mode 100644 index 00000000..5c75b1e5 --- /dev/null +++ b/tests/cs/csv-records.test.js @@ -0,0 +1,40 @@ +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { csvRecords } = require('../../tx/importers/csv-records'); + +async function records(text) { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'csv-records-')); + const file = path.join(dir, 'test.csv'); + fs.writeFileSync(file, text); + try { + const result = []; + for await (const r of csvRecords(file)) { + result.push(r); + } + return result; + } finally { + fs.rmSync(dir, { recursive: true, force: true }); + } +} + +describe('csvRecords', () => { + test('one record per line', async () => { + expect(await records('"a","b"\n"c","d"\n')).toEqual(['"a","b"', '"c","d"']); + }); + + test('a quoted field with a line break is one record', async () => { + // as in LOINC 2.83 ComponentHierarchyBySystem.csv (LP409416-7) + expect(await records('"h1","h2"\r\n"1","\r\nBCKDHB gene | Mutations"\r\n"2","x"\r\n')) + .toEqual(['"h1","h2"', '"1","\nBCKDHB gene | Mutations"', '"2","x"']); + }); + + test('escaped quotes do not start or end a field', async () => { + expect(await records('"a ""b"" c","d"\n"e","f ""g\nh"" i"\n"j"\n')) + .toEqual(['"a ""b"" c","d"', '"e","f ""g\nh"" i"', '"j"']); + }); + + test('an unterminated last record is still returned', async () => { + expect(await records('"a"\n"b\n')).toEqual(['"a"', '"b']); + }); +}); diff --git a/tests/data/cs-icf-test.json b/tests/data/cs-icf-test.json index 38b15a00..0217a691 100644 --- a/tests/data/cs-icf-test.json +++ b/tests/data/cs-icf-test.json @@ -57,7 +57,7 @@ { "code": "kind", "uri": "http://hl7.org/fhir/sid/icf#kind", - "description": "Whether the concept is a primary ICF category (primary), or a qualifier value, and if so which qualifier scale (extent, nature, localization, problem)", + "description": "Whether the concept is a primary ICF category (primary), or a qualifier value, and if so which qualifier scale (extent, nature, localization, problem, barrier, facilitator)", "type": "code" }, { @@ -101,6 +101,18 @@ "uri": "http://hl7.org/fhir/sid/icf#performance-without-assistance", "description": "Performance without assistance: the optional fourth qualifier on an activities and participation (d) code (a q4 code)", "type": "code" + }, + { + "code": "barrier", + "uri": "http://hl7.org/fhir/sid/icf#barrier", + "description": "Barrier: the qualifier on an environmental factor (e) code written with '.' (a q5 code)", + "type": "code" + }, + { + "code": "facilitator", + "uri": "http://hl7.org/fhir/sid/icf#facilitator", + "description": "Facilitator: the qualifier on an environmental factor (e) code written with '+' (a q6 code)", + "type": "code" } ], "concept": [ @@ -1158,6 +1170,283 @@ "valueCode": "problem" } ] + }, + { + "code": "q5", + "display": "Extent of barrier", + "definition": "The qualifier on an environmental factor (e) that is a barrier, written after a '.'", + "property": [ + { + "code": "notSelectable", + "valueBoolean": true + }, + { + "code": "kind", + "valueCode": "barrier" + } + ] + }, + { + "code": "q5-0", + "display": "NO barrier", + "definition": "0-4%", + "property": [ + { + "code": "parent", + "valueCode": "q5" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "barrier" + } + ] + }, + { + "code": "q5-1", + "display": "MILD barrier", + "definition": "5-24%", + "property": [ + { + "code": "parent", + "valueCode": "q5" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "barrier" + } + ] + }, + { + "code": "q5-2", + "display": "MODERATE barrier", + "definition": "25-49%", + "property": [ + { + "code": "parent", + "valueCode": "q5" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "barrier" + } + ] + }, + { + "code": "q5-3", + "display": "SEVERE barrier", + "definition": "50-95%", + "property": [ + { + "code": "parent", + "valueCode": "q5" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "barrier" + } + ] + }, + { + "code": "q5-4", + "display": "COMPLETE barrier", + "definition": "96-100%", + "property": [ + { + "code": "parent", + "valueCode": "q5" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "barrier" + } + ] + }, + { + "code": "q5-8", + "display": "barrier, not specified", + "definition": "Used when the factor is a barrier, but information is insufficient to grade it", + "property": [ + { + "code": "parent", + "valueCode": "q5" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "barrier" + } + ] + }, + { + "code": "q5-9", + "display": "not applicable", + "definition": "Used when it is inappropriate to apply the qualifier", + "property": [ + { + "code": "parent", + "valueCode": "q5" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "barrier" + } + ] + }, + { + "code": "q6", + "display": "Extent of facilitator", + "definition": "The qualifier on an environmental factor (e) that is a facilitator, written after a '+' in place of the '.'", + "property": [ + { + "code": "notSelectable", + "valueBoolean": true + }, + { + "code": "kind", + "valueCode": "facilitator" + } + ] + }, + { + "code": "q6-0", + "display": "NO facilitator", + "definition": "0-4%", + "property": [ + { + "code": "parent", + "valueCode": "q6" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "facilitator" + } + ] + }, + { + "code": "q6-1", + "display": "MILD facilitator", + "definition": "5-24%", + "property": [ + { + "code": "parent", + "valueCode": "q6" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "facilitator" + } + ] + }, + { + "code": "q6-2", + "display": "MODERATE facilitator", + "definition": "25-49%", + "property": [ + { + "code": "parent", + "valueCode": "q6" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "facilitator" + } + ] + }, + { + "code": "q6-3", + "display": "SUBSTANTIAL facilitator", + "definition": "50-95%", + "property": [ + { + "code": "parent", + "valueCode": "q6" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "facilitator" + } + ] + }, + { + "code": "q6-4", + "display": "COMPLETE facilitator", + "definition": "96-100%", + "property": [ + { + "code": "parent", + "valueCode": "q6" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "facilitator" + } + ] + }, + { + "code": "q6-8", + "display": "facilitator, not specified", + "definition": "Used when the factor is a facilitator, but information is insufficient to grade it", + "property": [ + { + "code": "parent", + "valueCode": "q6" + }, + { + "code": "notSelectable", + "valueBoolean": false + }, + { + "code": "kind", + "valueCode": "facilitator" + } + ] } ] } \ No newline at end of file diff --git a/tests/server/daily-report.test.js b/tests/server/daily-report.test.js index 9f6bece0..c34bb4b8 100644 --- a/tests/server/daily-report.test.js +++ b/tests/server/daily-report.test.js @@ -4,7 +4,7 @@ const path = require('path'); const Database = require('better-sqlite3'); const DailyReport = require('../../library/daily-report'); -const { nextMidnight, localDate, formatSpan } = require('../../library/daily-report'); +const { nextMidnight, localDate, formatSpan, describeSpan } = require('../../library/daily-report'); const Mailer = require('../../library/mailer'); const ServerStats = require('../../stats'); @@ -110,9 +110,11 @@ describe('daily report', () => { expect(text).not.toContain('## silent'); expect(text).toContain("## broken\n\nThe broken module's report failed: no database"); expect(text).toContain('## System'); - expect(text).toContain('FHIRsmith v0.17.1 on Node.js'); + expect(text).toContain('Versions: FHIRsmith = v0.17.1. Node = v'); expect(text).toContain('log file'); - expect(text).toContain('99.5% of the time'); + expect(text).toContain('Uptime: This run = '); + expect(text).toContain('. All runs, 99.5%'); + expect(text).toContain('Space: '); }); test('run sends the report and records when, and the next covers the time since', async () => { @@ -191,6 +193,15 @@ describe('daily report', () => { expect(localDate(midnight - 1)).toBe('2026-10-22'); }); + test('the title names the moment and the period', async () => { + const now = new Date(2026, 9, 24, 0, 0).getTime(); + const text = await newReport({}, { stats: statsWith([], 1) }).build(now - 24 * HOUR, now); + expect(text.split('\n')[0]).toBe('# tx.test.org daily report for 2026-10-24 00:00 covering the last 24 hours'); + expect(describeSpan(23.6 * HOUR)).toBe('24 hours'); + expect(describeSpan(30 * 60000)).toBe('1 hour'); + expect(describeSpan(3 * 24 * HOUR + 2 * HOUR)).toBe('3 days'); + }); + test('spans are to the minute', () => { expect(formatSpan(0)).toBe('less than a minute'); expect(formatSpan(90 * 1000)).toBe('2m'); diff --git a/tests/tx/closure-enabled.test.js b/tests/tx/closure-enabled.test.js index 3d117ee6..bfe7825c 100644 --- a/tests/tx/closure-enabled.test.js +++ b/tests/tx/closure-enabled.test.js @@ -220,6 +220,53 @@ describe('$closure (enabled)', () => { }); }); + // not in the specification: the client tells the server it has finished with the table + describe('disposing', () => { + const dispose = { name: 'dispose', valueBoolean: true }; + + test('dispose = true deletes the table', async () => { + await post(app, params(name('dispose-table'), reset, txResource)); + await post(app, params(name('dispose-table'), concept('code2'))); + const res = await post(app, params(name('dispose-table'), dispose)); + expect(res.status).toBe(200); + expect(res.body.resourceType).toBe('ConceptMap'); + expect(res.body.version).toBe('0'); + expect(res.body.group).toBeUndefined(); + const after = await post(app, params(name('dispose-table'), concept('code2a'))); + expect(after.status).toBe(404); + // gone, so it can be initialised again without reset + const again = await post(app, params(name('dispose-table'), txResource)); + expect(again.status).toBe(200); + expect(again.body.version).toBe('0'); + }); + + test('disposing of a table that does not exist is fine', async () => { + const res = await post(app, params(name('never-made'), dispose)); + expect(res.status).toBe(200); + expect(res.body.version).toBe('0'); + }); + + test('dispose can only name the table', async () => { + await post(app, params(name('dispose-keep'), reset, txResource)); + for (const other of [concept('code2'), { name: 'version', valueString: '0' }, reset, txResource]) { + const res = await post(app, params(name('dispose-keep'), dispose, other)); + expect(res.status).toBe(400); + expect(issueText(res)).toMatch(/disposes/); + } + // and the table is still there + const res = await post(app, params(name('dispose-keep'), concept('code2'))); + expect(res.status).toBe(200); + expect(res.body.version).toBe('1'); + }); + + test('dispose = false is the same as no dispose', async () => { + await post(app, params(name('dispose-false'), reset, txResource)); + const res = await post(app, params(name('dispose-false'), { name: 'dispose', valueBoolean: false }, concept('code2'))); + expect(res.status).toBe(200); + expect(res.body.version).toBe('1'); + }); + }); + describe('errors', () => { beforeAll(async () => { await post(app, params(name('err-table'), reset, txResource)); @@ -557,6 +604,18 @@ describe('ClosureStore', () => { ]); }); + test('dropTable deletes a table and everything in it, and says whether there was one', () => { + const t = store.createTable('t', [{ name: 'tx-resource', resource: { resourceType: 'CodeSystem', url: 'http://a' } }]); + store.recordAdd(t, [{ key: 'a', system: 'http://a', code: 'a' }, { key: 'b', system: 'http://a', code: 'b' }], + [{ source: 'a', target: 'b', relationship: 'source-is-narrower-than-target' }]); + expect(store.dropTable('t')).toBe(true); + expect(store.getTable('t')).toBeNull(); + expect(store.getParams(t.id)).toEqual([]); + expect(store.getConcepts(t.id)).toEqual([]); + expect(store.entryCount(t.id)).toBe(0); + expect(store.dropTable('t')).toBe(false); + }); + test('recreating a table drops its concepts, entries and parameters', () => { let t = store.createTable('t', [{ name: 'useSupplement', valueCanonical: 'http://s' }]); store.recordAdd(t, [{ key: 'a', system: 's', code: 'a' }, { key: 'b', system: 's', code: 'b' }], diff --git a/tests/tx/test-cases.test.js b/tests/tx/test-cases.test.js index 094c14aa..193c4536 100644 --- a/tests/tx/test-cases.test.js +++ b/tests/tx/test-cases.test.js @@ -5255,6 +5255,22 @@ describe('tx.fhir.org', () => { await runTest({"suite":"tx.fhir.org","test":"loinc-lookup-answer"}, "4.0"); }); + it("loinc-lookup-part-mappings" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-lookup-part-mappings"}, "5.0"); + }); + + it("loinc-lookup-part-mappings" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-lookup-part-mappings"}, "4.0"); + }); + + it("loinc-lookup-answer-race" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-lookup-answer-race"}, "5.0"); + }); + + it("loinc-lookup-answer-race" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-lookup-answer-race"}, "4.0"); + }); + it("loinc-validate-code" + 'R5', async () => { await runTest({"suite":"tx.fhir.org","test":"loinc-validate-code"}, "5.0"); }); @@ -5599,6 +5615,38 @@ describe('tx.fhir.org', () => { await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-dockind-bad"}, "4.0"); }); + it("loinc-expand-filter-no-method" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-expand-filter-no-method"}, "5.0"); + }); + + it("loinc-expand-filter-no-method" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-expand-filter-no-method"}, "4.0"); + }); + + it("loinc-validate-filter-no-method-good" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-good"}, "5.0"); + }); + + it("loinc-validate-filter-no-method-good" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-good"}, "4.0"); + }); + + it("loinc-validate-filter-no-method-method-bad" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-method-bad"}, "5.0"); + }); + + it("loinc-validate-filter-no-method-method-bad" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-method-bad"}, "4.0"); + }); + + it("loinc-validate-filter-no-method-part-bad" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-part-bad"}, "5.0"); + }); + + it("loinc-validate-filter-no-method-part-bad" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-part-bad"}, "4.0"); + }); + it("loinc-validate-filter-classtype-good" + 'R5', async () => { await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-classtype-good"}, "5.0"); }); @@ -5823,6 +5871,38 @@ describe('tx.fhir.org', () => { await runTest({"suite":"tx.fhir.org","test":"loinc-subsumes-unknown-code"}, "4.0"); }); + it("loinc-translate-part-sct" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-part-sct"}, "5.0"); + }); + + it("loinc-translate-part-sct" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-part-sct"}, "4.0"); + }); + + it("loinc-translate-sct-part" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-sct-part"}, "5.0"); + }); + + it("loinc-translate-sct-part" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-sct-part"}, "4.0"); + }); + + it("loinc-translate-part-sct-url" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-part-sct-url"}, "5.0"); + }); + + it("loinc-translate-part-sct-url" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-part-sct-url"}, "4.0"); + }); + + it("loinc-translate-code-mdc" + 'R5', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-code-mdc"}, "5.0"); + }); + + it("loinc-translate-code-mdc" + 'R4', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-code-mdc"}, "4.0"); + }); + }); describe('snomed', () => { @@ -6404,6 +6484,78 @@ describe('snomed', () => { await runTest({"suite":"snomed","test":"subsumes-expr-subsumes-precoordinated"}, "4.0"); }); + it("snomed-translate-edition-intl" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-intl"}, "5.0"); + }); + + it("snomed-translate-edition-intl" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-intl"}, "4.0"); + }); + + it("snomed-translate-edition-module" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-module"}, "5.0"); + }); + + it("snomed-translate-edition-module" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-module"}, "4.0"); + }); + + it("snomed-translate-edition-ungrouped" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-ungrouped"}, "5.0"); + }); + + it("snomed-translate-edition-ungrouped" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-ungrouped"}, "4.0"); + }); + + it("snomed-translate-edition-grouped" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-grouped"}, "5.0"); + }); + + it("snomed-translate-edition-grouped" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-grouped"}, "4.0"); + }); + + it("snomed-translate-edition-equivalent" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-equivalent"}, "5.0"); + }); + + it("snomed-translate-edition-equivalent" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-equivalent"}, "4.0"); + }); + + it("snomed-translate-edition-primitive" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-primitive"}, "5.0"); + }); + + it("snomed-translate-edition-primitive" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-primitive"}, "4.0"); + }); + + it("snomed-translate-edition-toplevel" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-toplevel"}, "5.0"); + }); + + it("snomed-translate-edition-toplevel" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-toplevel"}, "4.0"); + }); + + it("snomed-translate-edition-inactive-none" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-inactive-none"}, "5.0"); + }); + + it("snomed-translate-edition-inactive-none" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-inactive-none"}, "4.0"); + }); + + it("snomed-translate-edition-inactive-replaced" + 'R5', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-inactive-replaced"}, "5.0"); + }); + + it("snomed-translate-edition-inactive-replaced" + 'R4', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-inactive-replaced"}, "4.0"); + }); + }); describe('sct-ecl', () => { @@ -7105,6 +7257,278 @@ describe('sct-ecl', () => { await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-ambiguous-precedence"}, "4.0"); }); + it("snomed-expand-ecl-member-filter-misplaced" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-misplaced"}, "5.0"); + }); + + it("snomed-expand-ecl-member-filter-misplaced" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-misplaced"}, "4.0"); + }); + + it("snomed-expand-ecl-member-filter-date-in" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-date-in"}, "5.0"); + }); + + it("snomed-expand-ecl-member-filter-date-in" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-date-in"}, "4.0"); + }); + + it("snomed-expand-ecl-member-filter-date-out" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-date-out"}, "5.0"); + }); + + it("snomed-expand-ecl-member-filter-date-out" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-date-out"}, "4.0"); + }); + + it("snomed-expand-ecl-operator-on-set" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-set"}, "5.0"); + }); + + it("snomed-expand-ecl-operator-on-set" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-set"}, "4.0"); + }); + + it("snomed-expand-ecl-operator-on-set-ancestors" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-set-ancestors"}, "5.0"); + }); + + it("snomed-expand-ecl-operator-on-set-ancestors" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-set-ancestors"}, "4.0"); + }); + + it("snomed-expand-ecl-operator-on-memberOf" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-memberOf"}, "5.0"); + }); + + it("snomed-expand-ecl-operator-on-memberOf" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-memberOf"}, "4.0"); + }); + + it("snomed-expand-ecl-top-of-set" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-top-of-set"}, "5.0"); + }); + + it("snomed-expand-ecl-top-of-set" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-top-of-set"}, "4.0"); + }); + + it("snomed-expand-ecl-top-of-set-children" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-top-of-set-children"}, "5.0"); + }); + + it("snomed-expand-ecl-top-of-set-children" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-top-of-set-children"}, "4.0"); + }); + + it("snomed-expand-ecl-bottom-of-set" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-bottom-of-set"}, "5.0"); + }); + + it("snomed-expand-ecl-bottom-of-set" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-bottom-of-set"}, "4.0"); + }); + + it("snomed-expand-ecl-refinement-or" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-or"}, "5.0"); + }); + + it("snomed-expand-ecl-refinement-or" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-or"}, "4.0"); + }); + + it("snomed-expand-ecl-refinement-and" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-and"}, "5.0"); + }); + + it("snomed-expand-ecl-refinement-and" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-and"}, "4.0"); + }); + + it("snomed-expand-ecl-refinement-group-or" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-group-or"}, "5.0"); + }); + + it("snomed-expand-ecl-refinement-group-or" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-group-or"}, "4.0"); + }); + + it("snomed-expand-ecl-refinement-or-in-group" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-or-in-group"}, "5.0"); + }); + + it("snomed-expand-ecl-refinement-or-in-group" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-or-in-group"}, "4.0"); + }); + + it("snomed-expand-ecl-attribute-name-expression" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-expression"}, "5.0"); + }); + + it("snomed-expand-ecl-attribute-name-expression" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-expression"}, "4.0"); + }); + + it("snomed-expand-ecl-attribute-name-set" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-set"}, "5.0"); + }); + + it("snomed-expand-ecl-attribute-name-set" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-set"}, "4.0"); + }); + + it("snomed-expand-ecl-attribute-name-wildcard" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-wildcard"}, "5.0"); + }); + + it("snomed-expand-ecl-attribute-name-wildcard" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-wildcard"}, "4.0"); + }); + + it("snomed-expand-ecl-dotted-expression-attribute" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-dotted-expression-attribute"}, "5.0"); + }); + + it("snomed-expand-ecl-dotted-expression-attribute" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-dotted-expression-attribute"}, "4.0"); + }); + + it("snomed-expand-ecl-comment" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-comment"}, "5.0"); + }); + + it("snomed-expand-ecl-comment" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-comment"}, "4.0"); + }); + + it("snomed-expand-ecl-concept-filter-primitive" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-primitive"}, "5.0"); + }); + + it("snomed-expand-ecl-concept-filter-primitive" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-primitive"}, "4.0"); + }); + + it("snomed-expand-ecl-concept-filter-defined" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-defined"}, "5.0"); + }); + + it("snomed-expand-ecl-concept-filter-defined" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-defined"}, "4.0"); + }); + + it("snomed-expand-ecl-concept-filter-effectiveTime" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-effectiveTime"}, "5.0"); + }); + + it("snomed-expand-ecl-concept-filter-effectiveTime" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-effectiveTime"}, "4.0"); + }); + + it("snomed-expand-ecl-concept-filter-inactive" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-inactive"}, "5.0"); + }); + + it("snomed-expand-ecl-concept-filter-inactive" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-inactive"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-term" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-term" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-term-set" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term-set"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-term-set" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term-set"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-term-wild" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term-wild"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-term-wild" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term-wild"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-typeId" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-typeId"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-typeId" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-typeId"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-typeId-fsn" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-typeId-fsn"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-typeId-fsn" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-typeId-fsn"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-language" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-language"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-language" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-language"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-language-en" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-language-en"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-language-en" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-language-en"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-active" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-active"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-active" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-active"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-active-default" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-active-default"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-active-default" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-active-default"}, "4.0"); + }); + + it("snomed-expand-ecl-description-filter-two-blocks" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-two-blocks"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-two-blocks" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-two-blocks"}, "4.0"); + }); + + it("snomed-expand-ecl-memberOf-descriptions" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-memberOf-descriptions"}, "5.0"); + }); + + it("snomed-expand-ecl-memberOf-descriptions" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-memberOf-descriptions"}, "4.0"); + }); + + it("snomed-expand-ecl-memberOf-field-selection" + 'R5', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-memberOf-field-selection"}, "5.0"); + }); + + it("snomed-expand-ecl-memberOf-field-selection" + 'R4', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-memberOf-field-selection"}, "4.0"); + }); + it("snomed-expand-ecl-term-match" + 'R5', async () => { await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-term-match"}, "5.0"); }); @@ -9689,6 +10113,22 @@ describe('icd-11', () => { await runTest({"suite":"icd-11","test":"lookup-mms-source"}, "4.0"); }); + it("lookup-mms-source-details" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"lookup-mms-source-details"}, "5.0"); + }); + + it("lookup-mms-source-details" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"lookup-mms-source-details"}, "4.0"); + }); + + it("lookup-icf-source" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"lookup-icf-source"}, "5.0"); + }); + + it("lookup-icf-source" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"lookup-icf-source"}, "4.0"); + }); + it("lookup-mms-es" + 'R5', async () => { await runTest({"suite":"icd-11","test":"lookup-mms-es"}, "5.0"); }); @@ -9745,6 +10185,62 @@ describe('icd-11', () => { await runTest({"suite":"icd-11","test":"subsumes-unrelated"}, "4.0"); }); + it("translate-ciel1" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel1"}, "5.0"); + }); + + it("translate-ciel1" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel1"}, "4.0"); + }); + + it("translate-ciel1a" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel1a"}, "5.0"); + }); + + it("translate-ciel1a" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel1a"}, "4.0"); + }); + + it("translate-ciel2a" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel2a"}, "5.0"); + }); + + it("translate-ciel2a" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel2a"}, "4.0"); + }); + + it("translate-ciel2b" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel2b"}, "5.0"); + }); + + it("translate-ciel2b" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel2b"}, "4.0"); + }); + + it("translate-ciel3a" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel3a"}, "5.0"); + }); + + it("translate-ciel3a" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel3a"}, "4.0"); + }); + + it("translate-ciel3b" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel3b"}, "5.0"); + }); + + it("translate-ciel3b" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel3b"}, "4.0"); + }); + + it("translate-ciel4" + 'R5', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel4"}, "5.0"); + }); + + it("translate-ciel4" + 'R4', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel4"}, "4.0"); + }); + }); describe('regex-bad', () => { @@ -10469,20 +10965,505 @@ describe('closure', () => { await runTest({"suite":"closure","test":"closure-add-code2aII"}, "4.0"); }); - it("closure-add-code2aI-again" + 'R4', async () => { - await runTest({"suite":"closure","test":"closure-add-code2aI-again"}, "4.0"); + it("closure-add-code2aI-again" + 'R4', async () => { + await runTest({"suite":"closure","test":"closure-add-code2aI-again"}, "4.0"); + }); + + it("closure-add-code2b" + 'R4', async () => { + await runTest({"suite":"closure","test":"closure-add-code2b"}, "4.0"); + }); + + it("closure-resync-2" + 'R4', async () => { + await runTest({"suite":"closure","test":"closure-resync-2"}, "4.0"); + }); + + it("closure-resync-0" + 'R4', async () => { + await runTest({"suite":"closure","test":"closure-resync-0"}, "4.0"); + }); + +}); + +describe('icf', () => { + // Tests for ICF (2001a) qualifiers: the .XYZ grammar (extent, nature, localization; 8 = not specified), the displays it generates, enumerated value sets with qualified codes, and value set filters that require or forbid qualifiers + + it("icf-cs-plain" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-plain"}, "5.0"); + }); + + it("icf-cs-plain" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-plain"}, "4.0"); + }); + + it("icf-cs-extent" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent"}, "5.0"); + }); + + it("icf-cs-extent" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent"}, "4.0"); + }); + + it("icf-cs-extent-0" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-0"}, "5.0"); + }); + + it("icf-cs-extent-0" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-0"}, "4.0"); + }); + + it("icf-cs-extent-4" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-4"}, "5.0"); + }); + + it("icf-cs-extent-4" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-4"}, "4.0"); + }); + + it("icf-cs-extent-9" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-9"}, "5.0"); + }); + + it("icf-cs-extent-9" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-9"}, "4.0"); + }); + + it("icf-cs-long-code" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-long-code"}, "5.0"); + }); + + it("icf-cs-long-code" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-long-code"}, "4.0"); + }); + + it("icf-cs-three" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-three"}, "5.0"); + }); + + it("icf-cs-three" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-three"}, "4.0"); + }); + + it("icf-cs-two" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-two"}, "5.0"); + }); + + it("icf-cs-two" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-two"}, "4.0"); + }); + + it("icf-cs-eights" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-eights"}, "5.0"); + }); + + it("icf-cs-eights" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-eights"}, "4.0"); + }); + + it("icf-cs-eight-first" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-eight-first"}, "5.0"); + }); + + it("icf-cs-eight-first" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-eight-first"}, "4.0"); + }); + + it("icf-cs-display-good" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-good"}, "5.0"); + }); + + it("icf-cs-display-good" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-good"}, "4.0"); + }); + + it("icf-cs-display-category" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-category"}, "5.0"); + }); + + it("icf-cs-display-category" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-category"}, "4.0"); + }); + + it("icf-cs-display-bad" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-bad"}, "5.0"); + }); + + it("icf-cs-display-bad" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-bad"}, "4.0"); + }); + + it("icf-cs-display-eights" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-eights"}, "5.0"); + }); + + it("icf-cs-display-eights" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-eights"}, "4.0"); + }); + + it("icf-cs-bad-value" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-bad-value"}, "5.0"); + }); + + it("icf-cs-bad-value" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-bad-value"}, "4.0"); + }); + + it("icf-cs-too-many" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-too-many"}, "5.0"); + }); + + it("icf-cs-too-many" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-too-many"}, "4.0"); + }); + + it("icf-cs-not-digits" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-not-digits"}, "5.0"); + }); + + it("icf-cs-not-digits" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-not-digits"}, "4.0"); + }); + + it("icf-cs-empty" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-empty"}, "5.0"); + }); + + it("icf-cs-empty" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-empty"}, "4.0"); + }); + + it("icf-cs-heading-chapter" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-heading-chapter"}, "5.0"); + }); + + it("icf-cs-heading-chapter" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-heading-chapter"}, "4.0"); + }); + + it("icf-cs-heading-block" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-heading-block"}, "5.0"); + }); + + it("icf-cs-heading-block" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-heading-block"}, "4.0"); + }); + + it("icf-cs-unknown-category" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-unknown-category"}, "5.0"); + }); + + it("icf-cs-unknown-category" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-unknown-category"}, "4.0"); + }); + + it("icf-cs-b-too-many" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-b-too-many"}, "5.0"); + }); + + it("icf-cs-b-too-many" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-b-too-many"}, "4.0"); + }); + + it("icf-cs-d-performance" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-performance"}, "5.0"); + }); + + it("icf-cs-d-performance" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-performance"}, "4.0"); + }); + + it("icf-cs-d-two" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-two"}, "5.0"); + }); + + it("icf-cs-d-two" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-two"}, "4.0"); + }); + + it("icf-cs-d-four" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-four"}, "5.0"); + }); + + it("icf-cs-d-four" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-four"}, "4.0"); + }); + + it("icf-cs-d-eights" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-eights"}, "5.0"); + }); + + it("icf-cs-d-eights" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-eights"}, "4.0"); + }); + + it("icf-cs-d-too-many" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-too-many"}, "5.0"); + }); + + it("icf-cs-d-too-many" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-too-many"}, "4.0"); + }); + + it("icf-cs-d-bad-capacity" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-bad-capacity"}, "5.0"); + }); + + it("icf-cs-d-bad-capacity" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-bad-capacity"}, "4.0"); + }); + + it("icf-cs-e-barrier" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-e-barrier"}, "5.0"); + }); + + it("icf-cs-e-barrier" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-e-barrier"}, "4.0"); + }); + + it("icf-cs-facilitator" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-cs-facilitator"}, "5.0"); + }); + + it("icf-cs-facilitator" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-cs-facilitator"}, "4.0"); + }); + + it("icf-vs-enum-plain" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-plain"}, "5.0"); + }); + + it("icf-vs-enum-plain" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-plain"}, "4.0"); + }); + + it("icf-vs-enum-qualified" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-qualified"}, "5.0"); + }); + + it("icf-vs-enum-qualified" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-qualified"}, "4.0"); + }); + + it("icf-vs-enum-eights" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-eights"}, "5.0"); + }); + + it("icf-vs-enum-eights" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-eights"}, "4.0"); + }); + + it("icf-vs-enum-other-qualifier" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-other-qualifier"}, "5.0"); + }); + + it("icf-vs-enum-other-qualifier" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-other-qualifier"}, "4.0"); + }); + + it("icf-vs-enum-unlisted-eights" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-unlisted-eights"}, "5.0"); + }); + + it("icf-vs-enum-unlisted-eights" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-unlisted-eights"}, "4.0"); + }); + + it("icf-vs-all-plain" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-plain"}, "5.0"); + }); + + it("icf-vs-all-plain" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-plain"}, "4.0"); + }); + + it("icf-vs-all-qualified" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-qualified"}, "5.0"); + }); + + it("icf-vs-all-qualified" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-qualified"}, "4.0"); + }); + + it("icf-vs-all-other-component" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-other-component"}, "5.0"); + }); + + it("icf-vs-all-other-component" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-other-component"}, "4.0"); + }); + + it("icf-vs-extent-req-good" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-good"}, "5.0"); + }); + + it("icf-vs-extent-req-good" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-good"}, "4.0"); + }); + + it("icf-vs-extent-req-nine" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-nine"}, "5.0"); + }); + + it("icf-vs-extent-req-nine" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-nine"}, "4.0"); + }); + + it("icf-vs-extent-req-plain" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-plain"}, "5.0"); + }); + + it("icf-vs-extent-req-plain" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-plain"}, "4.0"); + }); + + it("icf-vs-extent-req-eight" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-eight"}, "5.0"); + }); + + it("icf-vs-extent-req-eight" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-eight"}, "4.0"); + }); + + it("icf-vs-extent-req-heading" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-heading"}, "5.0"); + }); + + it("icf-vs-extent-req-heading" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-heading"}, "4.0"); + }); + + it("icf-vs-full-good" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-good"}, "5.0"); + }); + + it("icf-vs-full-good" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-good"}, "4.0"); + }); + + it("icf-vs-full-missing" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-missing"}, "5.0"); + }); + + it("icf-vs-full-missing" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-missing"}, "4.0"); + }); + + it("icf-vs-full-eight" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-eight"}, "5.0"); + }); + + it("icf-vs-full-eight" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-eight"}, "4.0"); + }); + + it("icf-vs-full-nature-eight" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-nature-eight"}, "5.0"); + }); + + it("icf-vs-full-nature-eight" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-nature-eight"}, "4.0"); + }); + + it("icf-vs-full-plain" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-plain"}, "5.0"); + }); + + it("icf-vs-full-plain" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-plain"}, "4.0"); + }); + + it("icf-vs-noloc-plain" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-plain"}, "5.0"); + }); + + it("icf-vs-noloc-plain" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-plain"}, "4.0"); + }); + + it("icf-vs-noloc-two" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-two"}, "5.0"); + }); + + it("icf-vs-noloc-two" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-two"}, "4.0"); + }); + + it("icf-vs-noloc-eight" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-eight"}, "5.0"); + }); + + it("icf-vs-noloc-eight" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-eight"}, "4.0"); + }); + + it("icf-vs-noloc-present" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-present"}, "5.0"); + }); + + it("icf-vs-noloc-present" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-present"}, "4.0"); + }); + + it("icf-vs-capacity-good" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-good"}, "5.0"); + }); + + it("icf-vs-capacity-good" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-good"}, "4.0"); + }); + + it("icf-vs-capacity-performance-only" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-performance-only"}, "5.0"); + }); + + it("icf-vs-capacity-performance-only" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-performance-only"}, "4.0"); + }); + + it("icf-vs-capacity-eight" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-eight"}, "5.0"); + }); + + it("icf-vs-capacity-eight" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-eight"}, "4.0"); + }); + + it("icf-vs-capacity-plain" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-plain"}, "5.0"); + }); + + it("icf-vs-capacity-plain" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-plain"}, "4.0"); + }); + + it("icf-vs-noqual-plain" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-plain"}, "5.0"); + }); + + it("icf-vs-noqual-plain" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-plain"}, "4.0"); + }); + + it("icf-vs-noqual-qualified" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-qualified"}, "5.0"); + }); + + it("icf-vs-noqual-qualified" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-qualified"}, "4.0"); + }); + + it("icf-vs-noqual-eights" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-eights"}, "5.0"); }); - it("closure-add-code2b" + 'R4', async () => { - await runTest({"suite":"closure","test":"closure-add-code2b"}, "4.0"); + it("icf-vs-noqual-eights" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-eights"}, "4.0"); }); - it("closure-resync-2" + 'R4', async () => { - await runTest({"suite":"closure","test":"closure-resync-2"}, "4.0"); + it("icf-expand-enumerated" + 'R5', async () => { + await runTest({"suite":"icf","test":"icf-expand-enumerated"}, "5.0"); }); - it("closure-resync-0" + 'R4', async () => { - await runTest({"suite":"closure","test":"closure-resync-0"}, "4.0"); + it("icf-expand-enumerated" + 'R4', async () => { + await runTest({"suite":"icf","test":"icf-expand-enumerated"}, "4.0"); }); }); @@ -13186,6 +14167,14 @@ describe('tx.fhir.org', () => { await runTest({"suite":"tx.fhir.org","test":"loinc-lookup-answer"}, "5.0"); }); + it("loinc-lookup-part-mappings" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-lookup-part-mappings"}, "5.0"); + }); + + it("loinc-lookup-answer-race" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-lookup-answer-race"}, "5.0"); + }); + it("loinc-validate-code" + 'R5-cached', async () => { await runTest({"suite":"tx.fhir.org","test":"loinc-validate-code"}, "5.0"); }); @@ -13358,6 +14347,22 @@ describe('tx.fhir.org', () => { await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-dockind-bad"}, "5.0"); }); + it("loinc-expand-filter-no-method" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-expand-filter-no-method"}, "5.0"); + }); + + it("loinc-validate-filter-no-method-good" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-good"}, "5.0"); + }); + + it("loinc-validate-filter-no-method-method-bad" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-method-bad"}, "5.0"); + }); + + it("loinc-validate-filter-no-method-part-bad" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-no-method-part-bad"}, "5.0"); + }); + it("loinc-validate-filter-classtype-good" + 'R5-cached', async () => { await runTest({"suite":"tx.fhir.org","test":"loinc-validate-filter-classtype-good"}, "5.0"); }); @@ -13470,6 +14475,22 @@ describe('tx.fhir.org', () => { await runTest({"suite":"tx.fhir.org","test":"loinc-subsumes-unknown-code"}, "5.0"); }); + it("loinc-translate-part-sct" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-part-sct"}, "5.0"); + }); + + it("loinc-translate-sct-part" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-sct-part"}, "5.0"); + }); + + it("loinc-translate-part-sct-url" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-part-sct-url"}, "5.0"); + }); + + it("loinc-translate-code-mdc" + 'R5-cached', async () => { + await runTest({"suite":"tx.fhir.org","test":"loinc-translate-code-mdc"}, "5.0"); + }); + }); describe('snomed', () => { @@ -13763,6 +14784,42 @@ describe('snomed', () => { await runTest({"suite":"snomed","test":"subsumes-expr-subsumes-precoordinated"}, "5.0"); }); + it("snomed-translate-edition-intl" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-intl"}, "5.0"); + }); + + it("snomed-translate-edition-module" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-module"}, "5.0"); + }); + + it("snomed-translate-edition-ungrouped" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-ungrouped"}, "5.0"); + }); + + it("snomed-translate-edition-grouped" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-grouped"}, "5.0"); + }); + + it("snomed-translate-edition-equivalent" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-equivalent"}, "5.0"); + }); + + it("snomed-translate-edition-primitive" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-primitive"}, "5.0"); + }); + + it("snomed-translate-edition-toplevel" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-toplevel"}, "5.0"); + }); + + it("snomed-translate-edition-inactive-none" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-inactive-none"}, "5.0"); + }); + + it("snomed-translate-edition-inactive-replaced" + 'R5-cached', async () => { + await runTest({"suite":"snomed","test":"snomed-translate-edition-inactive-replaced"}, "5.0"); + }); + }); describe('sct-ecl', () => { @@ -14116,6 +15173,142 @@ describe('sct-ecl', () => { await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-ambiguous-precedence"}, "5.0"); }); + it("snomed-expand-ecl-member-filter-misplaced" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-misplaced"}, "5.0"); + }); + + it("snomed-expand-ecl-member-filter-date-in" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-date-in"}, "5.0"); + }); + + it("snomed-expand-ecl-member-filter-date-out" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-member-filter-date-out"}, "5.0"); + }); + + it("snomed-expand-ecl-operator-on-set" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-set"}, "5.0"); + }); + + it("snomed-expand-ecl-operator-on-set-ancestors" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-set-ancestors"}, "5.0"); + }); + + it("snomed-expand-ecl-operator-on-memberOf" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-operator-on-memberOf"}, "5.0"); + }); + + it("snomed-expand-ecl-top-of-set" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-top-of-set"}, "5.0"); + }); + + it("snomed-expand-ecl-top-of-set-children" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-top-of-set-children"}, "5.0"); + }); + + it("snomed-expand-ecl-bottom-of-set" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-bottom-of-set"}, "5.0"); + }); + + it("snomed-expand-ecl-refinement-or" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-or"}, "5.0"); + }); + + it("snomed-expand-ecl-refinement-and" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-and"}, "5.0"); + }); + + it("snomed-expand-ecl-refinement-group-or" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-group-or"}, "5.0"); + }); + + it("snomed-expand-ecl-refinement-or-in-group" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-refinement-or-in-group"}, "5.0"); + }); + + it("snomed-expand-ecl-attribute-name-expression" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-expression"}, "5.0"); + }); + + it("snomed-expand-ecl-attribute-name-set" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-set"}, "5.0"); + }); + + it("snomed-expand-ecl-attribute-name-wildcard" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-attribute-name-wildcard"}, "5.0"); + }); + + it("snomed-expand-ecl-dotted-expression-attribute" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-dotted-expression-attribute"}, "5.0"); + }); + + it("snomed-expand-ecl-comment" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-comment"}, "5.0"); + }); + + it("snomed-expand-ecl-concept-filter-primitive" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-primitive"}, "5.0"); + }); + + it("snomed-expand-ecl-concept-filter-defined" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-defined"}, "5.0"); + }); + + it("snomed-expand-ecl-concept-filter-effectiveTime" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-effectiveTime"}, "5.0"); + }); + + it("snomed-expand-ecl-concept-filter-inactive" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-concept-filter-inactive"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-term" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-term-set" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term-set"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-term-wild" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-term-wild"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-typeId" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-typeId"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-typeId-fsn" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-typeId-fsn"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-language" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-language"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-language-en" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-language-en"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-active" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-active"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-active-default" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-active-default"}, "5.0"); + }); + + it("snomed-expand-ecl-description-filter-two-blocks" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-description-filter-two-blocks"}, "5.0"); + }); + + it("snomed-expand-ecl-memberOf-descriptions" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-memberOf-descriptions"}, "5.0"); + }); + + it("snomed-expand-ecl-memberOf-field-selection" + 'R5-cached', async () => { + await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-memberOf-field-selection"}, "5.0"); + }); + it("snomed-expand-ecl-term-match" + 'R5-cached', async () => { await runTest({"suite":"sct-ecl","test":"snomed-expand-ecl-term-match"}, "5.0"); }); @@ -15436,6 +16629,14 @@ describe('icd-11', () => { await runTest({"suite":"icd-11","test":"lookup-mms-source"}, "5.0"); }); + it("lookup-mms-source-details" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"lookup-mms-source-details"}, "5.0"); + }); + + it("lookup-icf-source" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"lookup-icf-source"}, "5.0"); + }); + it("lookup-mms-es" + 'R5-cached', async () => { await runTest({"suite":"icd-11","test":"lookup-mms-es"}, "5.0"); }); @@ -15464,6 +16665,34 @@ describe('icd-11', () => { await runTest({"suite":"icd-11","test":"subsumes-unrelated"}, "5.0"); }); + it("translate-ciel1" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel1"}, "5.0"); + }); + + it("translate-ciel1a" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel1a"}, "5.0"); + }); + + it("translate-ciel2a" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel2a"}, "5.0"); + }); + + it("translate-ciel2b" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel2b"}, "5.0"); + }); + + it("translate-ciel3a" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel3a"}, "5.0"); + }); + + it("translate-ciel3b" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel3b"}, "5.0"); + }); + + it("translate-ciel4" + 'R5-cached', async () => { + await runTest({"suite":"icd-11","test":"translate-ciel4"}, "5.0"); + }); + }); describe('regex-bad', () => { @@ -15846,6 +17075,251 @@ describe('closure', () => { }); +describe('icf', () => { + // Tests for ICF (2001a) qualifiers: the .XYZ grammar (extent, nature, localization; 8 = not specified), the displays it generates, enumerated value sets with qualified codes, and value set filters that require or forbid qualifiers + + it("icf-cs-plain" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-plain"}, "5.0"); + }); + + it("icf-cs-extent" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent"}, "5.0"); + }); + + it("icf-cs-extent-0" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-0"}, "5.0"); + }); + + it("icf-cs-extent-4" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-4"}, "5.0"); + }); + + it("icf-cs-extent-9" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-extent-9"}, "5.0"); + }); + + it("icf-cs-long-code" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-long-code"}, "5.0"); + }); + + it("icf-cs-three" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-three"}, "5.0"); + }); + + it("icf-cs-two" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-two"}, "5.0"); + }); + + it("icf-cs-eights" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-eights"}, "5.0"); + }); + + it("icf-cs-eight-first" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-eight-first"}, "5.0"); + }); + + it("icf-cs-display-good" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-good"}, "5.0"); + }); + + it("icf-cs-display-category" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-category"}, "5.0"); + }); + + it("icf-cs-display-bad" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-bad"}, "5.0"); + }); + + it("icf-cs-display-eights" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-display-eights"}, "5.0"); + }); + + it("icf-cs-bad-value" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-bad-value"}, "5.0"); + }); + + it("icf-cs-too-many" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-too-many"}, "5.0"); + }); + + it("icf-cs-not-digits" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-not-digits"}, "5.0"); + }); + + it("icf-cs-empty" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-empty"}, "5.0"); + }); + + it("icf-cs-heading-chapter" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-heading-chapter"}, "5.0"); + }); + + it("icf-cs-heading-block" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-heading-block"}, "5.0"); + }); + + it("icf-cs-unknown-category" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-unknown-category"}, "5.0"); + }); + + it("icf-cs-b-too-many" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-b-too-many"}, "5.0"); + }); + + it("icf-cs-d-performance" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-performance"}, "5.0"); + }); + + it("icf-cs-d-two" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-two"}, "5.0"); + }); + + it("icf-cs-d-four" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-four"}, "5.0"); + }); + + it("icf-cs-d-eights" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-eights"}, "5.0"); + }); + + it("icf-cs-d-too-many" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-too-many"}, "5.0"); + }); + + it("icf-cs-d-bad-capacity" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-d-bad-capacity"}, "5.0"); + }); + + it("icf-cs-e-barrier" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-e-barrier"}, "5.0"); + }); + + it("icf-cs-facilitator" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-cs-facilitator"}, "5.0"); + }); + + it("icf-vs-enum-plain" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-plain"}, "5.0"); + }); + + it("icf-vs-enum-qualified" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-qualified"}, "5.0"); + }); + + it("icf-vs-enum-eights" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-eights"}, "5.0"); + }); + + it("icf-vs-enum-other-qualifier" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-other-qualifier"}, "5.0"); + }); + + it("icf-vs-enum-unlisted-eights" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-enum-unlisted-eights"}, "5.0"); + }); + + it("icf-vs-all-plain" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-plain"}, "5.0"); + }); + + it("icf-vs-all-qualified" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-qualified"}, "5.0"); + }); + + it("icf-vs-all-other-component" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-all-other-component"}, "5.0"); + }); + + it("icf-vs-extent-req-good" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-good"}, "5.0"); + }); + + it("icf-vs-extent-req-nine" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-nine"}, "5.0"); + }); + + it("icf-vs-extent-req-plain" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-plain"}, "5.0"); + }); + + it("icf-vs-extent-req-eight" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-eight"}, "5.0"); + }); + + it("icf-vs-extent-req-heading" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-extent-req-heading"}, "5.0"); + }); + + it("icf-vs-full-good" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-good"}, "5.0"); + }); + + it("icf-vs-full-missing" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-missing"}, "5.0"); + }); + + it("icf-vs-full-eight" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-eight"}, "5.0"); + }); + + it("icf-vs-full-nature-eight" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-nature-eight"}, "5.0"); + }); + + it("icf-vs-full-plain" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-full-plain"}, "5.0"); + }); + + it("icf-vs-noloc-plain" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-plain"}, "5.0"); + }); + + it("icf-vs-noloc-two" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-two"}, "5.0"); + }); + + it("icf-vs-noloc-eight" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-eight"}, "5.0"); + }); + + it("icf-vs-noloc-present" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-noloc-present"}, "5.0"); + }); + + it("icf-vs-capacity-good" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-good"}, "5.0"); + }); + + it("icf-vs-capacity-performance-only" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-performance-only"}, "5.0"); + }); + + it("icf-vs-capacity-eight" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-eight"}, "5.0"); + }); + + it("icf-vs-capacity-plain" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-capacity-plain"}, "5.0"); + }); + + it("icf-vs-noqual-plain" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-plain"}, "5.0"); + }); + + it("icf-vs-noqual-qualified" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-qualified"}, "5.0"); + }); + + it("icf-vs-noqual-eights" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-vs-noqual-eights"}, "5.0"); + }); + + it("icf-expand-enumerated" + 'R5-cached', async () => { + await runTest({"suite":"icf","test":"icf-expand-enumerated"}, "5.0"); + }); + +}); + }); }); diff --git a/translations/Messages.properties b/translations/Messages.properties index a051da85..3f10c049 100644 --- a/translations/Messages.properties +++ b/translations/Messages.properties @@ -1129,6 +1129,7 @@ CLOSURE_NAME_NOT_UUID = The closure table name ''{0}'' is not valid: this server CLOSURE_PARAM_UNKNOWN = The parameter ''{0}'' is not supported by $closure on this server CLOSURE_CONCEPT_AND_VERSION = A $closure request can add concepts or resynchronise from a version, but not both at once CLOSURE_RESET_AND_VERSION = A $closure request can reset the closure table or resynchronise from a version, but not both at once +CLOSURE_DISPOSE_NOT_ALONE = A $closure request that disposes of the closure table can only name the table: it cannot also add concepts, resynchronise, reset or configure it CLOSURE_TABLE_EXISTS = The closure table ''{0}'' already exists. To discard its content and start again, include the parameter reset = true CLOSURE_TABLE_UNKNOWN = The closure table ''{0}'' does not exist on this server. Initialise it first, with a $closure request that names it and has no concepts CLOSURE_CONFIG_NOT_ALLOWED = The parameter ''{0}'' configures a closure table, so it can only be supplied when the table is initialised (or reset) diff --git a/tx/closure/closure-store.js b/tx/closure/closure-store.js index d1eadc32..d4f76dfe 100644 --- a/tx/closure/closure-store.js +++ b/tx/closure/closure-store.js @@ -286,6 +286,20 @@ class ClosureStore { return tx(); } + /** + * Delete a table, and everything in it. The caller holds the table's lock. + * @param {string} name + * @returns {boolean} whether there was a table to delete + */ + dropTable(name) { + const existing = this.stmt.getTable.get(name); + if (!existing) { + return false; + } + this.stmt.deleteTable.run(existing.id); + return true; + } + /** * The parameters the table was initialised with, as Parameters.parameter entries. */ diff --git a/tx/cs/cs-api.js b/tx/cs/cs-api.js index b83cd7bc..eb3698dc 100644 --- a/tx/cs/cs-api.js +++ b/tx/cs/cs-api.js @@ -1036,9 +1036,11 @@ class CodeSystemFactoryProvider { * @param source - the source system URI * @param sourceVersion - the version of the source coding (may be null) * @param dest - the target system URI, possibly with |version + * @param code - the code being translated, if known (under reverse, the target concept's + * code), so a provider can leave out a map that has nothing to say about it */ // eslint-disable-next-line no-unused-vars - async findImplicitConceptMaps(conceptMaps, source, sourceVersion, dest) { + async findImplicitConceptMaps(conceptMaps, source, sourceVersion, dest, code = null) { return null; } diff --git a/tx/cs/cs-icf.js b/tx/cs/cs-icf.js index 53a9a88d..02afe1a0 100644 --- a/tx/cs/cs-icf.js +++ b/tx/cs/cs-icf.js @@ -12,10 +12,12 @@ * s body structures extent, nature, localization * d activities/participation performance, capacity, then optionally capacity with * assistance and performance without assistance - * e environmental factors not supported yet (barriers '.', facilitators '+') + * e environmental factors one: a barrier after '.', or a facilitator after '+' (in + * place of the '.') - e310.2, e310+3 * Each digit must be a value of its position's scale, which the code system carries as the - * concepts q1-n (extent of impairment), q2-n (nature), q3-n (localization) and q4-n (extent of - * problem), under the group concepts q1..q4. A position cannot be omitted; 8 (not specified) is + * concepts q1-n (extent of impairment), q2-n (nature), q3-n (localization), q4-n (extent of + * problem), q5-n (barrier) and q6-n (facilitator, which has no 9), under the group concepts + * q1..q6. A position cannot be omitted; 8 (not specified) is * what fills it, and 8 says nothing in the display. Each position is reported by $lookup as the * property named in QUALIFIERS. * @@ -37,32 +39,40 @@ const { Issue } = require('../library/operation-outcome'); const { FhirCodeSystemProvider, FhirCodeSystemProviderContext, FhirCodeSystemProviderFilterContext } = require('./cs-cs'); /** - * The qualifier positions of each component: the property that reports the qualifier, the - * scale (group concept) its values come from, and the label that introduces its value in the - * display (none for b and s, whose qualifiers say what they are). + * The qualifier positions of each component, by the separator that introduces them: the + * property that reports the qualifier, the scale (group concept) its values come from, and the + * label that introduces its value in the display (none where the value says what it is). + * Only environmental factors take '+', which makes the qualifier a facilitator. */ const QUALIFIERS = { - b: [ - { property: 'extent', scale: 'q1' } - ], - s: [ - { property: 'extent', scale: 'q1' }, - { property: 'nature', scale: 'q2' }, - { property: 'localization', scale: 'q3' } - ], - d: [ - { property: 'performance', scale: 'q4', label: 'performance' }, - { property: 'capacity', scale: 'q4', label: 'capacity' }, - { property: 'capacity-with-assistance', scale: 'q4', label: 'capacity with assistance' }, - { property: 'performance-without-assistance', scale: 'q4', label: 'performance without assistance' } - ], - e: null // barriers and facilitators: not supported yet + b: { + '.': [{ property: 'extent', scale: 'q1' }] + }, + s: { + '.': [ + { property: 'extent', scale: 'q1' }, + { property: 'nature', scale: 'q2' }, + { property: 'localization', scale: 'q3' } + ] + }, + d: { + '.': [ + { property: 'performance', scale: 'q4', label: 'performance' }, + { property: 'capacity', scale: 'q4', label: 'capacity' }, + { property: 'capacity-with-assistance', scale: 'q4', label: 'capacity with assistance' }, + { property: 'performance-without-assistance', scale: 'q4', label: 'performance without assistance' } + ] + }, + e: { + '.': [{ property: 'barrier', scale: 'q5' }], + '+': [{ property: 'facilitator', scale: 'q6' }] + } }; const COMPONENT_NAMES = { b: 'body functions', s: 'body structures', d: 'activities and participation', e: 'environmental factors' }; -const COMPONENT_CODES = { b: 'a body functions (b) code', s: 'a body structures (s) code', d: 'an activities and participation (d) code' }; +const COMPONENT_CODES = { b: 'a body functions (b) code', s: 'a body structures (s) code', d: 'an activities and participation (d) code', e: 'an environmental factors (e) code' }; -const QUALIFIER_PROPERTIES = new Set(Object.values(QUALIFIERS).filter(q => q).flatMap(q => q.map(p => p.property))); +const QUALIFIER_PROPERTIES = new Set(Object.values(QUALIFIERS).flatMap(c => Object.values(c)).flatMap(q => q.map(p => p.property))); /** * A category with qualifiers. code and concept are the category's, so whatever is inherited @@ -110,21 +120,22 @@ class IcfCodeSystemProvider extends FhirCodeSystemProvider { // -- locating ------------------------------------------------------------------------- /** - * A code in the code system, or a selectable category followed by '.' and one to three - * qualifier digits. An unknown category is a plain miss (no message); a known category - * with bad qualifiers says what is wrong with them. + * A code in the code system, or a selectable category followed by '.' (or, for an + * environmental factor, '+') and its qualifier digits. An unknown category is a plain miss + * (no message); a known category with bad qualifiers says what is wrong with them. */ async locate(code) { const base = await super.locate(code); if (base.context || typeof code !== 'string') { return base; } - const dot = code.indexOf('.'); - if (dot <= 0) { + const sep = code.search(/[.+]/); + if (sep <= 0) { return base; } - const stemCode = code.substring(0, dot); - const digits = code.substring(dot + 1); + const separator = code[sep]; + const stemCode = code.substring(0, sep); + const digits = code.substring(sep + 1); const stem = this.codeSystem.getConceptByCode(stemCode); if (!stem) { return { context: null, message: undefined }; @@ -133,15 +144,17 @@ class IcfCodeSystemProvider extends FhirCodeSystemProvider { return { context: null, message: `The ICF code '${code}' is not valid: qualifiers can only follow a selectable ICF category, and '${stemCode}' is not one` }; } const component = stemCode[0]; - const positions = QUALIFIERS[component]; + const positions = (QUALIFIERS[component] || {})[separator]; if (!positions) { - return { context: null, message: `The ICF code '${code}' is not valid: qualifiers on ${COMPONENT_NAMES[component] || 'these'} codes are not supported` }; + return { context: null, message: separator === '+' + ? `The ICF code '${code}' is not valid: only an environmental factor (e) can be a facilitator, written with '+'` + : `The ICF code '${code}' is not valid: qualifiers on ${COMPONENT_NAMES[component] || 'these'} codes are not supported` }; } const names = positions.map(q => q.property).join(', '); if (!new RegExp(`^[0-9]{1,${positions.length}}$`).test(digits)) { return { context: null, message: positions.length === 1 - ? `The ICF code '${code}' is not valid: ${COMPONENT_CODES[component]} takes one qualifier digit after the '.' (${names})` - : `The ICF code '${code}' is not valid: ${COMPONENT_CODES[component]} takes 1 to ${positions.length} qualifier digits after the '.' (${names})` }; + ? `The ICF code '${code}' is not valid: ${COMPONENT_CODES[component]} takes one qualifier digit after the '${separator}' (${names})` + : `The ICF code '${code}' is not valid: ${COMPONENT_CODES[component]} takes 1 to ${positions.length} qualifier digits after the '${separator}' (${names})` }; } const qualifiers = []; for (let i = 0; i < digits.length; i++) { diff --git a/tx/cs/cs-loinc.js b/tx/cs/cs-loinc.js index 1b663b7c..30011089 100644 --- a/tx/cs/cs-loinc.js +++ b/tx/cs/cs-loinc.js @@ -7,6 +7,7 @@ const { validateOptionalParameter, validateArrayParameter} = require("../../libr const {BaseCSServices} = require("./cs-base"); const {sqlEscapeString} = require("../../xig/xig"); const regexUtilities = require('../../library/regex-utilities'); +const {ConceptMap} = require("../library/conceptmap"); // Context kinds matching Pascal enum const LoincProviderContextKind = { @@ -16,6 +17,42 @@ const LoincProviderContextKind = { ANSWER: 3 // lpckAnswer }; +// LOINC's mappings to other code systems (the ExternalCodes / ExternalMappings tables, from +// PartRelatedCodeMapping, the IEEE mapping table and the answer lists). Each target system +// has an implicit ConceptMap http://loinc.org/cm/, and its mappings are $lookup +// properties named . Targets in systems not listed here are not used yet +const LOINC_MAPS = [ + { system: 'http://loinc.org', key: 'loinc' }, + { system: 'http://pubchem.ncbi.nlm.nih.gov', key: 'ncbi' }, + { system: 'http://snomed.info/sct', key: 'sct' }, + { system: 'http://www.cms.gov/Medicare/Coding/HCPCSReleaseCodeSets', key: 'hcpcs' }, + { system: 'http://www.genenames.org', key: 'gene-names' }, + { system: 'http://www.nlm.nih.gov/research/umls/rxnorm', key: 'rxnorm' }, + { system: 'https://www.ebi.ac.uk/chebi', key: 'chebi' }, + { system: 'https://www.ncbi.nlm.nih.gov/clinvar', key: 'ncbi-clinvar' }, + { system: 'https://www.ncbi.nlm.nih.gov/gene', key: 'ncbi-gene' }, + { system: 'https://www.ncbi.nlm.nih.gov/taxonomy', key: 'ncbi-taxo' }, + { system: 'urn:iso:std:iso:11073:10101', key: 'mdc' }, + { system: 'urn:oid:2.16.840.1.113883.6.238', key: 'race' } +]; +const LOINC_MAP_BASE = 'http://loinc.org/cm/'; + +// The mapping tables hold R4 equivalences, stated from the LOINC code to the external code. +// Translations report R5 relationships, from the code being translated to the one found +function mapRelationship(equivalence, fromLoinc) { + switch (equivalence) { + case 'equivalent': + case 'equal': + return 'equivalent'; + case 'narrower': // the external code is narrower than the LOINC code + return fromLoinc ? 'source-is-broader-than-target' : 'source-is-narrower-than-target'; + case 'wider': + return fromLoinc ? 'source-is-narrower-than-target' : 'source-is-broader-than-target'; + default: + return 'related-to'; + } +} + const classTypes = { '1': 'Laboratory class', '2': 'Clinical class', @@ -127,6 +164,7 @@ class LoincServices extends BaseCSServices { this.propertyList = sharedData.propertyList; this.statusKeys = sharedData.statusKeys; this.statusCodes = sharedData.statusCodes; + this.hasMappings = sharedData.hasMappings; } close() { @@ -319,10 +357,114 @@ class LoincServices extends BaseCSServices { this.#addRelationshipProperties(ctxt, props, params), this.#addConceptProperties(ctxt, props,params), this.#addStatusProperty(ctxt, props,params), - this.#addRelatedNames(ctxt, props,params) + this.#addRelatedNames(ctxt, props,params), + this.#addMappingProperties(ctxt, props, params) ]); } + /** + * The code's mappings to other code systems, as Coding-valued properties named for the + * map (sct, chebi, mdc...), each with its equivalence as a subproperty. Returned by + * default, and when asked for by name + */ + async #addMappingProperties(ctxt, props, params) { + if (!this.hasMappings) { + return; + } + const rows = await this.#dbAll(` + SELECT ExternalCodes.System, ExternalCodes.Version, ExternalCodes.Code, ExternalCodes.Display, ExternalMappings.Equivalence + FROM ExternalMappings, ExternalCodes + WHERE ExternalMappings.CodeKey = ? + AND ExternalMappings.ExternalCodeKey = ExternalCodes.ExternalCodeKey + ORDER BY ExternalCodes.System, ExternalCodes.Code`, [ctxt.key]); + for (const row of rows) { + const map = LOINC_MAPS.find(m => m.system === row.System); + if (map && this._hasProp(props, map.key, true)) { + const coding = { system: row.System }; + if (row.Version) { + coding.version = row.Version; + } + coding.code = row.Code; + if (row.Display) { + coding.display = row.Display; + } + params.push({ + name: 'property', + part: [ + { name: 'code', valueCode: map.key }, + { name: 'value', valueCoding: coding }, + { name: 'subproperty', part: [{ name: 'code', valueCode: 'equivalence' }, { name: 'value', valueCode: row.Equivalence }] } + ] + }); + } + } + } + + #dbAll(sql, args) { + return new Promise((resolve, reject) => { + this.db.all(sql, args, (err, rows) => err ? reject(err) : resolve(rows)); + }); + } + + /** + * Translate using one of LOINC's implicit maps (http://loinc.org/cm/). The map works + * in both directions, decided by the system of the code being translated: a LOINC code to + * the codes it maps to in the map's system, or a code in that system to the LOINC codes + * that map to it (for the LOINC to LOINC map, always the first). The version of a non-LOINC + * code is not used: the mappings name one version of each external system + */ + async getTranslations(map, coding, target, reverse) { // eslint-disable-line no-unused-vars + const lm = map && map.jsonObj.loincMap; + if (!lm || !this.hasMappings) { + return []; + } + const targetSystem = target ? target.split('|')[0] : null; + const result = []; + if (coding.system === this.system() && (!targetSystem || targetSystem === lm.system)) { + const ctxt = this.codes.get(coding.code); + if (!ctxt) { + return []; + } + const rows = await this.#dbAll(` + SELECT ExternalCodes.Version, ExternalCodes.Code, ExternalCodes.Display, ExternalMappings.Equivalence + FROM ExternalMappings, ExternalCodes + WHERE ExternalMappings.CodeKey = ? + AND ExternalMappings.ExternalCodeKey = ExternalCodes.ExternalCodeKey + AND ExternalCodes.System = ? + ORDER BY ExternalCodes.Code`, [ctxt.key, lm.system]); + for (const row of rows) { + const t = { map: map.vurl, system: lm.system, code: row.Code, relationship: mapRelationship(row.Equivalence, true) }; + if (row.Version) { + t.version = row.Version; + } + if (row.Display) { + t.display = row.Display; + } + result.push(t); + } + } else if (coding.system === lm.system && (!targetSystem || targetSystem === this.system())) { + const rows = await this.#dbAll(` + SELECT Codes.Code, Codes.Description, ExternalMappings.Equivalence + FROM ExternalCodes, ExternalMappings, Codes + WHERE ExternalCodes.System = ? + AND ExternalCodes.Code = ? + AND ExternalMappings.ExternalCodeKey = ExternalCodes.ExternalCodeKey + AND Codes.CodeKey = ExternalMappings.CodeKey + ORDER BY Codes.Code`, [lm.system, coding.code]); + for (const row of rows) { + result.push({ + map: map.vurl, + system: this.system(), + version: this.version(), + code: row.Code, + display: row.Description, + relationship: mapRelationship(row.Equivalence, false) + }); + } + } + return result; + } + #getDesignationUse(kind) { switch (kind) { case LoincProviderContextKind.CODE: @@ -1178,6 +1320,9 @@ class LoincServicesFactory extends CodeSystemFactoryProvider { propertyList: new Map(), statusKeys: new Map(), statusCodes: new Map(), + hasMappings: false, + mappedCodes: new Map(), // LOINC code -> Set of the systems it maps to + mappedTargets: new Set(), // system|code of every external code something maps to _version: '', root: '', firstCodeKey: 0 @@ -1190,11 +1335,13 @@ class LoincServicesFactory extends CodeSystemFactoryProvider { this.#loadStatusCodes(db), this.#loadRelationshipTypes(db), this.#loadPropertyTypes(db), - this.#loadConfig(db) + this.#loadConfig(db), + this.#loadHasMappings(db) ]); // Load codes (largest operation) await this.#loadCodes(db); + await this.#loadMappingIndex(db); // Load dependent data in parallel await Promise.all([ @@ -1420,6 +1567,106 @@ class LoincServicesFactory extends CodeSystemFactoryProvider { return this._sharedData?._version || 'unknown'; } + // the mapping tables were added with the LOINC 2.83 importer; older databases don't have them + async #loadHasMappings(db) { + return new Promise((resolve, reject) => { + db.get("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'ExternalMappings'", (err, row) => { + if (err) { + reject(err); + } else { + this._sharedData.hasMappings = !!row; + resolve(); + } + }); + }); + } + + // which codes have mappings, so that a map is only offered for a code it says something about + async #loadMappingIndex(db) { + if (!this._sharedData.hasMappings) { + return; + } + return new Promise((resolve, reject) => { + db.all(`SELECT Codes.Code as LoincCode, ExternalCodes.System, ExternalCodes.Code + FROM ExternalMappings, ExternalCodes, Codes + WHERE ExternalMappings.ExternalCodeKey = ExternalCodes.ExternalCodeKey + AND ExternalMappings.CodeKey = Codes.CodeKey`, (err, rows) => { + if (err) { + reject(err); + } else { + for (const row of rows) { + let systems = this._sharedData.mappedCodes.get(row.LoincCode); + if (!systems) { + systems = new Set(); + this._sharedData.mappedCodes.set(row.LoincCode, systems); + } + systems.add(row.System); + this._sharedData.mappedTargets.add(row.System + '|' + row.Code); + } + resolve(); + } + }); + }); + } + + /** + * LOINC's implicit maps (see LOINC_MAPS): from LOINC to a system it maps to, or from that + * system to LOINC. When the code being translated is known, the map is only offered if it + * has a mapping for it - so a translation between LOINC and SNOMED CT that the SNOMED CT + * edition answers (the LOINC Extension) does not also report a LOINC map that says nothing. + * The code may be on either side (under reverse it is the target concept) + */ + async findImplicitConceptMaps(conceptMaps, source, sourceVersion, dest, code = null) { // eslint-disable-line no-unused-vars + await this.#ensureLoaded(); + if (!dest || !this._sharedData.hasMappings) { + return; + } + const destSystem = dest.split('|')[0]; + let other = null; + if (source === this.system()) { + other = destSystem; + } else if (destSystem === this.system()) { + other = source; + } + const lm = LOINC_MAPS.find(m => m.system === other); + if (!lm || conceptMaps.some(m => m.url === LOINC_MAP_BASE + lm.key)) { + return; + } + if (code && !(this._sharedData.mappedCodes.get(code)?.has(lm.system)) && !this._sharedData.mappedTargets.has(lm.system + '|' + code)) { + return; + } + conceptMaps.push(this.#loincMap(lm)); + } + + async findImplicitConceptMap(url, version) { + if (!url || !url.startsWith(LOINC_MAP_BASE)) { + return null; + } + await this.#ensureLoaded(); + if ((version && version !== this.version()) || !this._sharedData.hasMappings) { + return null; + } + const lm = LOINC_MAPS.find(m => LOINC_MAP_BASE + m.key === url); + return lm ? this.#loincMap(lm) : null; + } + + #loincMap(lm) { + const groups = [{ source: this.system(), target: lm.system }]; + if (lm.system !== this.system()) { + groups.push({ source: lm.system, target: this.system() }); + } + return new ConceptMap({ + resourceType: 'ConceptMap', + internalSource: this, + loincMap: lm, + url: LOINC_MAP_BASE + lm.key, + name: `LOINC ${lm.key} Concept Map`, + description: `The mappings between LOINC ${this.version()} and ${lm.system}, as published by LOINC`, + status: 'active', + group: groups + }); + } + async build(opContext, supplements) { await this.#ensureLoaded(); this.recordUse(); diff --git a/tx/cs/cs-snomed.js b/tx/cs/cs-snomed.js index b26ed658..167eac95 100644 --- a/tx/cs/cs-snomed.js +++ b/tx/cs/cs-snomed.js @@ -31,6 +31,18 @@ const Logger = require("../../library/logger"); // shared - it simply recomputes. const SNOMED_FILTER_ANALYSIS = Symbol('snomedFilterAnalysis'); +/** + * Alternate identifier schemes (sct2_Identifier) that FHIRsmith knows how to present: + * the code system the identifiers are codes in, and the $lookup property they are + * returned as. An identifier in an unknown scheme is kept but not reported. + */ +const IDENTIFIER_SCHEMES = { + '30051010000102': { system: 'http://loinc.org', property: 'loinc' } // |LOINC code identifier| (LOINC Extension) +}; + +/** The $expand / ECL-free filter on alternate identifiers: mapped: exists true|false */ +const MAPPED_FILTER_PREFIX = 'mapped:'; + // Description language index -> language code. The index is assigned at import // time by SnomedImporter.mapLanguageCode(); this table is its inverse and must // be kept in step with it. @@ -192,6 +204,47 @@ class SnomedServices { // means "could not decide", and leaves the value unchecked. this.expressionServices.eclConstraintMatcher = (constraint, value) => this.mrcmRangeSatisfied(constraint, value); this._mrcmRangeCache = new Map(); + + this.#loadIdentifiers(sharedData.identifiers || []); + } + + /** + * Index the alternate identifiers (sct2_Identifier rows, active only, as + * [scheme, module, code, conceptId]) both ways: concept -> identifiers, and + * scheme|code -> concepts. Also which modules carry identifiers, since each such + * module defines an implicit map (?fhir_cm=). + */ + #loadIdentifiers(rows) { + this.identifiersByConcept = new Map(); + this.conceptsByIdentifier = new Map(); + this.identifierModules = new Map(); // module -> Set of schemes + for (const [scheme, module, code, conceptId] of rows) { + const entry = { scheme, module, code }; + if (!this.identifiersByConcept.has(conceptId)) { + this.identifiersByConcept.set(conceptId, []); + } + this.identifiersByConcept.get(conceptId).push(entry); + const key = scheme + '|' + code; + if (!this.conceptsByIdentifier.has(key)) { + this.conceptsByIdentifier.set(key, []); + } + this.conceptsByIdentifier.get(key).push(conceptId); + if (!this.identifierModules.has(module)) { + this.identifierModules.set(module, new Set()); + } + this.identifierModules.get(module).add(scheme); + } + } + + /** The alternate identifiers of a concept (by id), optionally only those of one scheme */ + getIdentifiers(conceptId, scheme = null) { + const list = this.identifiersByConcept.get(String(conceptId)) || []; + return scheme ? list.filter(i => i.scheme === scheme) : list; + } + + /** The concept ids that have the given code in the given scheme */ + getConceptsForIdentifier(scheme, code) { + return this.conceptsByIdentifier.get(scheme + '|' + code) || []; } /** @@ -2707,34 +2760,46 @@ class SnomedProvider extends BaseCSServices { const ctxt = await this.#ensureContext(context); if (ctxt) { if (!(ctxt instanceof SnomedExpressionContext) || ctxt.expression?.concepts.length == 1) { - const time = this.sct.concepts.getConcept(ctxt.getReference()).effectiveTime; - // Pascal TDateTime epoch (1899-12-30) computed in UTC so the formatted - // date is timezone-independent. new Date(1899,11,30) is LOCAL midnight, - // which in zones ahead of UTC rolls the ISO date back by one day. - const pascalEpochUTC = Date.UTC(1899, 11, 30); - const date = new Date(pascalEpochUTC + time * 86400000); - const dateStr = date.toISOString().slice(0, 10); - this._addDateTimeProperty(params, 'property', 'effectiveTime', dateStr); - + // Only what was asked for, if anything was (see _hasProp): effectiveTime, module, + // parent, child and the defining relationships are returned by default but are not + // important enough to return when the client named the properties it wants. + // The mappings (alternate identifiers such as the LOINC code) are returned by + // default too, and when asked for by name. + if (this._hasProp(props, 'effectiveTime', true)) { + const time = this.sct.concepts.getConcept(ctxt.getReference()).effectiveTime; + // Pascal TDateTime epoch (1899-12-30) computed in UTC so the formatted + // date is timezone-independent. new Date(1899,11,30) is LOCAL midnight, + // which in zones ahead of UTC rolls the ISO date back by one day. + const pascalEpochUTC = Date.UTC(1899, 11, 30); + const date = new Date(pascalEpochUTC + time * 86400000); + const dateStr = date.toISOString().slice(0, 10); + this._addDateTimeProperty(params, 'property', 'effectiveTime', dateStr); + } - const parents = this.sct.getConceptParents(ctxt.getReference()); - for (let parentRef of parents) { - const code = this.sct.getConceptId(parentRef); - const description = this.sct.getDisplayName(parentRef); - this._addCodeProperty(params, 'property', 'parent', code, null, description); + if (this._hasProp(props, 'parent', true)) { + const parents = this.sct.getConceptParents(ctxt.getReference()); + for (let parentRef of parents) { + const code = this.sct.getConceptId(parentRef); + const description = this.sct.getDisplayName(parentRef); + this._addCodeProperty(params, 'property', 'parent', code, null, description); + } } - const children = this.sct.getConceptChildren(ctxt.getReference()); - for (let childRef of children) { - const code = this.sct.getConceptId(childRef); - const description = this.sct.getDisplayName(childRef); - this._addCodeProperty(params, 'property', 'child', code, null, description); + if (this._hasProp(props, 'child', true)) { + const children = this.sct.getConceptChildren(ctxt.getReference()); + for (let childRef of children) { + const code = this.sct.getConceptId(childRef); + const description = this.sct.getDisplayName(childRef); + this._addCodeProperty(params, 'property', 'child', code, null, description); + } } - const moduleId = this.sct.concepts.getModuleId(ctxt.getReference()); - if (moduleId) { - const code = this.sct.getConceptId(moduleId); - this._addCodeProperty(params, 'property', 'module', code, null, this.sct.getDisplayName(moduleId)); + if (this._hasProp(props, 'module', true)) { + const moduleId = this.sct.concepts.getModuleId(ctxt.getReference()); + if (moduleId) { + const code = this.sct.getConceptId(moduleId); + this._addCodeProperty(params, 'property', 'module', code, null, this.sct.getDisplayName(moduleId)); + } } const relationships = this.sct.getConceptRelationships(ctxt.getReference()); @@ -2742,7 +2807,7 @@ class SnomedProvider extends BaseCSServices { for (let relationshipRef of relationships) { const relationship = this.sct.relationships.getRelationship(relationshipRef); const relType = this.sct.getConceptId(relationship.relType); - if (relType != '116680003') { + if (relType != '116680003' && this._hasProp(props, relType, true)) { const relTypeD = this.sct.getDisplayName(relationship.relType); const code = this.sct.getConceptId(relationship.target); const description = this.sct.getDisplayName(relationship.target); @@ -2753,6 +2818,8 @@ class SnomedProvider extends BaseCSServices { } } } + + this.#addMappingProperties(ctxt, props, params); } if (ctxt instanceof SnomedExpressionContext) { // ignore concepts for now, but list refinements and refinement groups @@ -2768,6 +2835,27 @@ class SnomedProvider extends BaseCSServices { } } + /** + * The concept's alternate identifiers in the schemes we know (IDENTIFIER_SCHEMES), as + * Coding-valued properties named for the scheme - e.g. loinc = http://loinc.org#2160-0. + * Returned by default, and when asked for by name. + */ + #addMappingProperties(ctxt, props, params) { + const conceptId = this.sct.getConceptId(ctxt.getReference()); + for (const ident of this.sct.getIdentifiers(conceptId)) { + const scheme = IDENTIFIER_SCHEMES[ident.scheme]; + if (scheme && this._hasProp(props, scheme.property, true)) { + params.push({ + name: 'property', + part: [ + { name: 'code', valueCode: scheme.property }, + { name: 'value', valueCoding: { system: scheme.system, code: ident.code } } + ] + }); + } + } + } + /** * Report one refinement of a post-coordinated expression as a lookup property. * @@ -2823,6 +2911,11 @@ class SnomedProvider extends BaseCSServices { return true; } + if (prop.startsWith(MAPPED_FILTER_PREFIX)) { + return op === 'exists' && ['true', 'false'].includes(value) && + this.sct.stringToIdOrZero(prop.substring(MAPPED_FILTER_PREFIX.length)) !== 0n; + } + const cid = this.sct.stringToIdOrZero(prop); if (cid != 0) { const id = this.sct.stringToIdOrZero(value); @@ -2979,6 +3072,27 @@ class SnomedProvider extends BaseCSServices { return null; } + // mapped: exists true|false - concepts that do (or don't) have an + // alternate identifier in that scheme (e.g. mapped:30051010000102: has a LOINC code). + // A prefix, so as not to be confused with the attribute filters ( = ) + if (prop.startsWith(MAPPED_FILTER_PREFIX)) { + const scheme = prop.substring(MAPPED_FILTER_PREFIX.length); + if (this.sct.stringToIdOrZero(scheme) === 0n) { + throw new Error(`Invalid identifier scheme: ${scheme}`); + } + if (op !== 'exists') { + throw new Error(`Unsupported filter operation: ${prop} ${op} ${value}`); + } + if (value !== 'true' && value !== 'false') { + throw new Error(`Invalid filter value: ${value}`); + } + const filter = new SnomedFilterContext(); + filter.mappedScheme = scheme; + filter.mappedExists = value === 'true'; + filterContext.filters.push(filter); + return null; + } + const cid = this.sct.stringToIdOrZero(prop); if (cid != 0) { @@ -3082,7 +3196,9 @@ class SnomedProvider extends BaseCSServices { } let found = false; - if (set.inactive !== undefined) { + if (set.mappedScheme !== undefined) { + found = this.#isMapped(reference, set.mappedScheme) === set.mappedExists; + } else if (set.inactive !== undefined) { let concept = this.sct.concepts.getConcept(reference); let active = (concept.flags & 0x0F) === 0; found = active !== set.inactive @@ -3128,6 +3244,9 @@ class SnomedProvider extends BaseCSServices { } const reference = concept.getReference(); + if (set.mappedScheme !== undefined) { + return this.#isMapped(reference, set.mappedScheme) === set.mappedExists; + } if (set.inactive !== undefined) { return this.sct.isActive(reference) !== set.inactive; } @@ -3187,10 +3306,39 @@ class SnomedProvider extends BaseCSServices { return set._matchSet.has(reference); } + /** Whether the concept (by reference) has an alternate identifier in the scheme */ + #isMapped(reference, scheme) { + return this.sct.getIdentifiers(this.sct.getConceptId(reference), scheme).length > 0; + } + #ensurePopulated(set) { if (set.populationDone) { return; } + if (set.mappedScheme !== undefined) { + if (set.mappedExists) { + // straight from the identifier index, in concept order + const refs = new Set(); + for (const [conceptId, idents] of this.sct.identifiersByConcept) { + if (idents.some(i => i.scheme === set.mappedScheme)) { + const r = this.sct.concepts.findConcept(BigInt(conceptId)); + if (r.found) { + refs.add(r.index); + } + } + } + set.descendants = [...refs].sort((a, b) => a - b); + } else { + for (let i = 0; i < this.sct.concepts.count(); i++) { + const concept = this.sct.concepts.getConceptByCount(i); + if (!this.#isMapped(concept.index, set.mappedScheme)) { + set.descendants.push(concept.index); + } + } + } + set.populationDone = true; + return; + } if (set.inactive !== undefined && set.descendants.length === 0) { for (let i = 0; i < this.sct.concepts.count(); i++) { let concept = this.sct.concepts.getConceptByCount(i); @@ -3342,6 +3490,9 @@ class SnomedProvider extends BaseCSServices { } async getTranslations(map, coding, target, reverse) { + if (map && map.jsonObj.identifierMap) { + return this.translateByIdentifier(map, coding, target); + } if (!map || reverse) { return []; } @@ -3405,6 +3556,54 @@ class SnomedProvider extends BaseCSServices { return result; } + /** + * Translate using a module's alternate identifiers (the implicit map ?fhir_cm=, + * e.g. the LOINC Extension's LOINC codes). The identifiers say the concept and the code + * are the same thing, so the map works the same in both directions: a SNOMED CT concept + * translates to its codes, and a code in the identifier's system to its concepts. Either + * way the relationship is equivalent. + */ + async translateByIdentifier(map, coding, target) { + const { schemes } = map.jsonObj.identifierMap; + const targetSystem = target ? target.split('|')[0] : null; + const result = []; + if (coding.system === this.system()) { + const ctxt = await this.locate(coding.code); + if (!ctxt.context) { + return []; + } + const conceptId = this.sct.getConceptId(ctxt.context.getReference()); + for (const ident of this.sct.getIdentifiers(conceptId)) { + const scheme = IDENTIFIER_SCHEMES[ident.scheme]; + if (scheme && schemes.includes(ident.scheme) && (!targetSystem || targetSystem === scheme.system)) { + result.push({ + map: map.vurl, + system: scheme.system, + code: ident.code, + relationship: 'equivalent' + }); + } + } + } else if (!targetSystem || targetSystem === this.system()) { + for (const schemeId of schemes) { + const scheme = IDENTIFIER_SCHEMES[schemeId]; + if (scheme && scheme.system === coding.system) { + for (const conceptId of this.sct.getConceptsForIdentifier(schemeId, coding.code)) { + result.push({ + map: map.vurl, + system: this.system(), + version: this.version(), + code: conceptId, + display: await this.display(conceptId), + relationship: 'equivalent' + }); + } + } + } + } + return result; + } + /** * Translate a concept of this edition to the edition it depends on (typically a national * extension to the International Edition). There is no map: the answer comes from the @@ -3789,6 +3988,9 @@ class SnomedServicesFactory extends CodeSystemFactoryProvider { * edition URI plus ?fhir_cm=edition/ */ async findImplicitConceptMaps(conceptMaps, source, sourceVersion, dest) { + if (dest) { + this.#findIdentifierMaps(conceptMaps, source, sourceVersion, dest); + } if (source !== this.system() || !sourceVersion || !dest || !this.isMyEdition(sourceVersion)) { return; } @@ -3823,6 +4025,61 @@ class SnomedServicesFactory extends CodeSystemFactoryProvider { }); } + /** + * Maps implied by alternate identifiers, found from the systems rather than nominated: + * from this edition (source system|version naming it) to an identifier system (LOINC, + * say), or from an identifier system to this edition (the target given as + * system|edition URI, with or without the version) + */ + #findIdentifierMaps(conceptMaps, source, sourceVersion, dest) { + const bar = dest.indexOf('|'); + const destSystem = bar === -1 ? dest : dest.substring(0, bar); + const destVersion = bar === -1 ? null : dest.substring(bar + 1); + let identSystem; + if (source === this.system() && sourceVersion && this.isMyEdition(sourceVersion)) { + identSystem = destSystem; + } else if (destSystem === this.system() && destVersion && this.isMyEdition(destVersion)) { + identSystem = source; + } else { + return; + } + for (const [moduleId, schemes] of this.snomedServices.identifierModules) { + if ([...schemes].some(s => IDENTIFIER_SCHEMES[s] && IDENTIFIER_SCHEMES[s].system === identSystem)) { + const cm = this.identifierMap(moduleId); + if (cm && !conceptMaps.some(m => m.url === cm.url)) { + conceptMaps.push(cm); + } + } + } + } + + /** + * The implicit map defined by a module's alternate identifiers, if the module has any + * in a scheme we know. Its url follows the edition maps: /version/?fhir_cm= + */ + identifierMap(moduleId) { + const schemes = [...(this.snomedServices.identifierModules.get(moduleId) || [])].filter(s => IDENTIFIER_SCHEMES[s]); + if (schemes.length === 0) { + return null; + } + const systems = schemes.map(s => IDENTIFIER_SCHEMES[s].system); + const moduleName = this.snomedServices.conceptExists(moduleId) + ? this.snomedServices.getDisplayName(this.snomedServices.concepts.findConcept(BigInt(moduleId)).index) : moduleId; + return new ConceptMap({ + resourceType: 'ConceptMap', + internalSource: this, + identifierMap: { module: moduleId, schemes: schemes }, + url: `${this.version()}?fhir_cm=${moduleId}`, + name: `SNOMED CT ${moduleName} Concept Map`, + description: `The concept map implied by the alternate identifiers (${systems.join(', ')}) of the module ${moduleId} |${moduleName}| in ${this.version()}`, + status: 'active', + group: [ + ...systems.map(s => ({ source: this.system(), target: s })), + ...systems.map(s => ({ source: s, target: this.system() })) + ] + }); + } + /** Whether a source version names this edition: exactly, or the edition without a version */ isMyEdition(version) { const mine = this.version(); @@ -3846,6 +4103,16 @@ class SnomedServicesFactory extends CodeSystemFactoryProvider { const dep = this.snomedServices.editionDependency(`http://snomed.info/sct/${url.substring(q + '?fhir_cm=edition/'.length)}`); return dep ? this.editionTranslationMap(dep) : null; } + // ?fhir_cm=: the map implied by a module's alternate + // identifiers (e.g. 11010000107, the LOINC Extension's LOINC codes) + const qm = url.indexOf('?fhir_cm='); + if (qm !== -1 && url.substring(0, qm) !== this.system()) { + if (!this.isMyEdition(url.substring(0, qm))) { + return null; + } + await this.#ensureLoaded(); + return this.identifierMap(url.substring(qm + '?fhir_cm='.length)); + } if (!url.startsWith(this.system()+"?fhir_cm=")) { return null; } diff --git a/tx/data/loinc/AccessoryFiles/ComponentHierarchyBySystem/ComponentHierarchyBySystem.csv b/tx/data/loinc/AccessoryFiles/ComponentHierarchyBySystem/ComponentHierarchyBySystem.csv index 590f7a65..59d5f7d4 100644 --- a/tx/data/loinc/AccessoryFiles/ComponentHierarchyBySystem/ComponentHierarchyBySystem.csv +++ b/tx/data/loinc/AccessoryFiles/ComponentHierarchyBySystem/ComponentHierarchyBySystem.csv @@ -8,7 +8,8 @@ "LP432695-7.LP29693-6.LP343406-7.LP7819-8.LP14559-6.LP98185-9","1","LP98185-9","LP14082-9","Bacteria" "LP432695-7.LP29693-6.LP343406-7.LP7819-8.LP14559-6.LP98185-9.LP14082-9","1","LP14082-9","LP442038-8","Bacteria | Abscess | Microbiology" "LP432695-7.LP29693-6.LP343406-7.LP7819-8.LP14559-6.LP98185-9.LP14082-9.LP442038-8","1","LP442038-8","595-9","Bacteria Absc Aerobe Cult" -"LP432695-7.LP29693-6.LP343406-7.LP7819-8.LP14559-6.LP98185-9.LP14082-9.LP442038-8","2","LP442038-8","107188-5","Bacteria Absc Anaerobe Cult" +"LP432695-7.LP29693-6.LP343406-7.LP7819-8.LP14559-6.LP98185-9.LP14082-9.LP442038-8","2","LP442038-8","107188-5"," +Bacteria Absc Anaerobe Cult" "LP432695-7.LP29693-6.LP343406-7.LP7819-8.LP14559-6.LP98185-9.LP14082-9.LP442038-8","3","LP442038-8","75357-4","Bacteria Absc Anaerobe+Aerobe Cult" "LP432695-7.LP29693-6.LP343406-7.LP7819-8.LP14559-6","10","LP14559-6","LP442509-8","Observation" "LP432695-7.LP29693-6","3","LP29693-6","LP343631-0","Chemistry and Chemistry - challenge" diff --git a/tx/data/loinc/AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv b/tx/data/loinc/AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv new file mode 100644 index 00000000..4b8dda6c --- /dev/null +++ b/tx/data/loinc/AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv @@ -0,0 +1,3 @@ +"LOINC_NUM","LOINC_LONG_COMMON_NAME","IEEE_CF_CODE10","IEEE_REFID","EQUIVALENCE" +"6298-4","Potassium [Moles/volume] in Blood","160016","MDC_CONC_K_GEN","broader" +"75940-7","Potassium [Mass/volume] in Blood","160016","MDC_CONC_K_GEN","broader" diff --git a/tx/data/loinc/AccessoryFiles/PartFile/PartRelatedCodeMapping.csv b/tx/data/loinc/AccessoryFiles/PartFile/PartRelatedCodeMapping.csv new file mode 100644 index 00000000..1e125b92 --- /dev/null +++ b/tx/data/loinc/AccessoryFiles/PartFile/PartRelatedCodeMapping.csv @@ -0,0 +1,120 @@ +"PartNumber","PartName","PartTypeName","ExtCodeId","ExtCodeDisplayName","ExtCodeSystem","Equivalence","ContentOrigin","ExtCodeSystemVersion","ExtCodeSystemCopyrightNotice" +"LP14082-9","Bacteria","COMPONENT","2","Bacteria","https://www.ncbi.nlm.nih.gov/taxonomy","equivalent","","","" +"LP14082-9","Bacteria","COMPONENT","409822003","Domain Bacteria (organism)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP14355-9","Creatinine","COMPONENT","15373003","Creatinine (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP14355-9","Creatinine","COMPONENT","588","Creatinine","http://pubchem.ncbi.nlm.nih.gov","equivalent","","","" +"LP14355-9","Creatinine","COMPONENT","CHEBI:16737","creatinine","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP14492-0","Urea nitrogen","COMPONENT","95971004","Urea nitrogen (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP14551-3","Temazepam","COMPONENT","10355","Temazepam","http://www.nlm.nih.gov/research/umls/rxnorm","equivalent","","","" +"LP14551-3","Temazepam","COMPONENT","387300007","Temazepam (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP14551-3","Temazepam","COMPONENT","CHEBI:9435","Temazepam","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP14559-6","Microorganism","COMPONENT","264395009","Microorganism (organism)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP14559-6","Microorganism","COMPONENT","81726","unidentified microorganism","https://www.ncbi.nlm.nih.gov/taxonomy","equivalent","","","" +"LP14635-4","Glucose","COMPONENT","4850","Glucose","http://www.nlm.nih.gov/research/umls/rxnorm","equivalent","","","" +"LP14635-4","Glucose","COMPONENT","5793","Glucose","http://pubchem.ncbi.nlm.nih.gov","equivalent","","","" +"LP14635-4","Glucose","COMPONENT","67079006","Glucose (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP14635-4","Glucose","COMPONENT","CHEBI:17234","glucose","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP147650-8","Blatta orientalis Ab.IgE","COMPONENT","388387005","Blatta orientalis specific immunoglobulin E (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP14912-7","Phosphate","COMPONENT","1061","Phosphate","http://pubchem.ncbi.nlm.nih.gov","equivalent","","","" +"LP14912-7","Phosphate","COMPONENT","304053000","Phosphate electrolyte (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP14912-7","Phosphate","COMPONENT","CHEBI:26020","phosphate","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP15098-4","Potassium","COMPONENT","304048005","Potassium electrolyte (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP15098-4","Potassium","COMPONENT","5462222","Potassium","http://pubchem.ncbi.nlm.nih.gov","equivalent","","","" +"LP15098-4","Potassium","COMPONENT","8588","Potassium","http://www.nlm.nih.gov/research/umls/rxnorm","equivalent","","","" +"LP15098-4","Potassium","COMPONENT","CHEBI:26216","potassium atom","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP15455-6","Calcidiol","COMPONENT","259337002","Calcifediol (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP15455-6","Calcidiol","COMPONENT","5283731","Calcidiol","http://pubchem.ncbi.nlm.nih.gov","equivalent","","","" +"LP15455-6","Calcidiol","COMPONENT","CHEBI:17933","calcidiol","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP15942-3","Urobilinogen","COMPONENT","26818","Urobilinogen","http://pubchem.ncbi.nlm.nih.gov","equivalent","","","" +"LP15942-3","Urobilinogen","COMPONENT","44079009","Urobilinogen (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP15942-3","Urobilinogen","COMPONENT","CHEBI:29026","urobilinogen","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP19403-2","Electrolytes","COMPONENT","86355000","Electrolyte (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP19505-4","Diethylpropion","COMPONENT","3389","Diethylpropion","http://www.nlm.nih.gov/research/umls/rxnorm","equivalent","","","" +"LP19505-4","Diethylpropion","COMPONENT","387303009","Amfepramone (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP19505-4","Diethylpropion","COMPONENT","CHEBI:4530","diethylpropion","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP199977-2","Head","Rad.Anatomic Location.Region Imaged","RID9080","head","http://www.radlex.org","equivalent","","4.3","" +"LP199995-4","Neck","Rad.Anatomic Location.Region Imaged","RID7488","neck","http://www.radlex.org","equivalent","","4.3","" +"LP200067-9","CT","Rad.Modality.Modality Type","RID10321","computed tomography","http://www.radlex.org","equivalent","","4.3","" +"LP200068-7","Angio","Rad.Modality.Modality Subtype","RID10371","ANGIOGRAPHY","http://www.radlex.org","equivalent","","4.3","" +"LP200078-6","IV","Rad.Pharmaceutical.Route","RID11160","intravenous","http://www.radlex.org","equivalent","","4.3","" +"LP200085-1","Contrast","Rad.Pharmaceutical.Substance Given","RID11582","Contrast agent","http://www.radlex.org","equivalent","","4.3","" +"LP200088-5","W","Rad.Timing","RID49853","with","http://www.radlex.org","equivalent","","4.3","" +"LP200344-2","Circle of Willis","Rad.Anatomic Location.Imaging Focus","RID28600","Circle of Willis","http://www.radlex.org","equivalent","","4.3","" +"LP20174-6","adjusted","ADJUSTMENT","707682000","Adjusted measurement technique (qualifier value)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP208124-0","Carotid arteries","Rad.Anatomic Location.Imaging Focus","RID38751","Set of carotid arteries","http://www.radlex.org","equivalent","","4.3","" +"LP21304-8","Bld.dot","SYSTEM","440500007","Dried blood spot specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP21304-8","Bld.dot","SYSTEM","87612001","Blood (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP21418-6","Carboxyhemoglobin","COMPONENT","62576004","Carboxyhemoglobin (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP217195-9","PrThr","PROPERTY","705057003","Presence (property) (qualifier value)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP30226-2","RBC^BPU","SYSTEM","122582004","Erythrocyte specimen from blood product (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP30226-2","RBC^BPU","SYSTEM","410652009","Blood product (product)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP34404-1","1,1-Dichloroethylene","COMPONENT","83849008","1,1-Dichloroethylene (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP34404-1","1,1-Dichloroethylene","COMPONENT","CHEBI:34031","1,1-dichloroethene","https://www.ebi.ac.uk/chebi","equivalent","","","" +"LP36498-1","BAL","SYSTEM","258607008","Bronchoalveolar lavage fluid specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP36498-1","BAL","SYSTEM","87200008","Lower respiratory fluid (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP36995-6","Afipia felis Ab.IgM","COMPONENT","710584003","Immunoglobulin M antibody to Afipia felis (substance)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP37400-6","Bovine inner ear Ag","COMPONENT","115570000","Bovine heat shock protein antigen 70 (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP38212-4","G Ag","COMPONENT","21642002","Blood group antigen G (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP38335-3","Hepatitis C virus RNA","COMPONENT","121204002","Ribonucleic acid of Hepatitis C virus (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP38557-2","Human coronavirus NL63 RNA","COMPONENT","707894007","Ribonucleic acid of Human coronavirus NL63 (substance)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP38754-5","K little p super little a Ab","COMPONENT","5533005","Blood group antibody Kp^a^ (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP40159-3","West Nile virus Ab","COMPONENT","421862008","Antibody to West Nile virus (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP40193-2","Yellow fever virus Ab.IgG","COMPONENT","710550002","Immunoglobulin G antibody to Yellow fever virus (substance)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6103-8","Aerobic culture","METHOD","703750006","Aerobic culture technique (qualifier value)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6197-0","Confirm","METHOD","703690001","Confirmatory technique (qualifier value)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6213-5","Cyto stain","METHOD","702665008","Cytology stain technique (qualifier value)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6464-4","Probe.amp.tar","METHOD","702675006","Probe with target amplification technique (qualifier value)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6773-8","ACnc","PROPERTY","118569000","Arbitrary concentration (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6779-5","Aper","PROPERTY","718498007","Appearance (property) (qualifier value)","http://snomed.info/sct","equivalent","LN","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6819-9","Imp","PROPERTY","118580000","Impression AND/OR interpretation of study (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6827-2","MCnc","PROPERTY","118539007","Mass concentration (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6838-9","NFr","PROPERTY","118552002","Number fraction (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6850-4","Prid","PROPERTY","118584009","Presence OR identity (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6860-3","SCnc","PROPERTY","118556004","Substance concentration (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6870-2","Susc","PROPERTY","118588007","Susceptibility (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6879-3","Time","PROPERTY","410669006","Time (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6881-9","Titr","PROPERTY","118590008","Dilution factor expressed as titer (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6886-8","Type","PROPERTY","410656007","Type (property) (qualifier value)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6886-8","Type","PROPERTY","718499004","Color (property) (qualifier value)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6888-4","Vel","PROPERTY","118592000","Velocity (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6893-4","Vol","PROPERTY","118565006","Volume (property) (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6960-1","Pt","TIME","123029007","Single point in time (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6994-0","Abscess","SYSTEM","119371008","Specimen from abscess (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP6994-0","Abscess","SYSTEM","44132006","Abscess (morphologic abnormality)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7057-5","Bld","SYSTEM","119297000","Blood specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7057-5","Bld","SYSTEM","87612001","Blood (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7068-2","BldC","SYSTEM","122554006","Capillary blood specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7068-2","BldC","SYSTEM","87612001","Blood (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7073-2","BldV","SYSTEM","122555007","Venous blood specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7073-2","BldV","SYSTEM","87612001","Blood (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7156-5","CSF","SYSTEM","258450006","Cerebrospinal fluid specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7156-5","CSF","SYSTEM","65216001","Cerebrospinal fluid (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7255-5","Gast fld","SYSTEM","258459007","Gastric fluid specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7255-5","Gast fld","SYSTEM","31773000","Gastric juice (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7370-2","Isolate","SYSTEM","119303007","Microbial isolate specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7370-2","Isolate","SYSTEM","386126001","Microbial isolate (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7486-6","Plr fld","SYSTEM","2778004","Pleural fluid (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7486-6","Plr fld","SYSTEM","418564007","Pleural fluid specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7567-3","Ser","SYSTEM","119364003","Serum specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7567-3","Ser","SYSTEM","50863008","Plasma (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7576-4","Ser/Plas","SYSTEM","122592007","Acellular blood (serum or plasma) specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7576-4","Ser/Plas","SYSTEM","50863008","Plasma (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7592-1","Synv fld","SYSTEM","119332005","Synovial fluid specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7592-1","Synv fld","SYSTEM","6085005","Synovial fluid (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7604-4","Stool","SYSTEM","119339001","Stool specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7604-4","Stool","SYSTEM","256867001","Gastrointestinal tract material (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7681-2","Urine","SYSTEM","276833005","24 hour urine specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7681-2","Urine","SYSTEM","278020009","Spot urine specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7681-2","Urine","SYSTEM","442043001","2 hour urine specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7681-2","Urine","SYSTEM","78014005","Urine (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7715-8","Vitr fld","SYSTEM","258438000","Vitreous humor specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7715-8","Vitr fld","SYSTEM","426101004","Vitreous humor (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7719-0","Water","SYSTEM","11713004","Water (substance)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7719-0","Water","SYSTEM","119318008","Water specimen (specimen)","http://snomed.info/sct","narrower","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7735-6","XXX","SYSTEM","123038009","Specimen (specimen)","http://snomed.info/sct","wider","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7749-7","Nar","SCALE","117364006","Narrative value (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7750-5","Nom","SCALE","117362005","Nominal value (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7751-3","Ord","SCALE","117363000","Ordinal value (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7752-1","OrdQn","SCALE","117365007","Ordinal OR quantitative value (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP7753-9","Qn","SCALE","30766002","Quantitative value (qualifier value)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." +"LP89244-5","Autoantibodies","COMPONENT","30621004","Autoantibody (substance)","http://snomed.info/sct","equivalent","Both","http://snomed.info/sct/900000000000207008/version/20260101","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. ""SNOMED"" and ""SNOMED CT"" are registered trademarks of the IHTSDO. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to https://www.snomed.org/get-snomed or info@snomed.org. This may incur a fee in SNOMED International non-Member countries." diff --git a/tx/data/snomed/Refset/Language/der2_cRefset_LanguageSnapshot-en_LO1010000_20250909.txt b/tx/data/snomed/Refset/Language/der2_cRefset_LanguageSnapshot-en_LO1010000_20250909.txt new file mode 100644 index 00000000..7df4f443 --- /dev/null +++ b/tx/data/snomed/Refset/Language/der2_cRefset_LanguageSnapshot-en_LO1010000_20250909.txt @@ -0,0 +1,77 @@ +id effectiveTime active moduleId refsetId referencedComponentId acceptabilityId +018e2dfe-71be-4ec2-ad93-a10a2a6cdd1a 20250909 1 11010000107 900000000000509007 4600151010000111 900000000000549004 +0326c378-e612-498b-9cff-db21dc48b800 20250909 0 11010000107 900000000000509007 2524551010000117 900000000000549004 +06b1d33f-707b-45b1-9310-155c37bea220 20250909 1 11010000107 900000000000509007 943471010000118 900000000000548007 +0a746aae-9176-434d-809f-36d5f3c0e670 20250909 1 11010000107 900000000000509007 645471010000115 900000000000548007 +0dc18475-21d9-42e8-8020-ada6ca31c3b6 20250909 1 11010000107 900000000000509007 5088731010000118 900000000000549004 +10192985-0951-4b9b-a927-c8c37e965487 20250909 1 11010000107 900000000000509007 155431010000117 900000000000548007 +122208ca-8642-4fdb-ae1a-195270b72927 20250909 1 11010000107 900000000000509007 465051010000112 900000000000548007 +1576ce86-6272-4292-96cd-d093b65d1d68 20250909 1 11010000107 900000000000509007 21010000117 900000000000548007 +1dab2220-8bc1-4fa0-8ba5-4a4db7ca756a 20250909 1 11010000107 900000000000509007 943481010000115 900000000000549004 +1e6d6e82-e791-4ffb-a45e-a69e5e282cc9 20250909 1 11010000107 900000000000509007 707641010000118 900000000000549004 +28715017-0154-4732-91e0-ca3fff0f63d3 20250909 1 11010000107 900000000000509007 645461010000113 900000000000549004 +2f83c9ea-ea57-4272-8cd9-96dcf64931ef 20250909 1 11010000107 900000000000509007 155441010000113 900000000000549004 +31f4310c-5941-4d36-a728-e519a8e2873a 20250909 1 11010000107 900000000000509007 707611010000117 900000000000548007 +335c492a-b638-42df-9cac-e323c3578af8 20250909 1 11010000107 900000000000509007 2422581010000112 900000000000548007 +3b04876b-be18-4c29-8e5b-73c62812cb4f 20250909 1 11010000107 900000000000509007 827321010000113 900000000000549004 +4043215e-50a7-43c7-926b-a5821694965b 20250909 1 11010000107 900000000000509007 4670061010000117 900000000000549004 +40880faa-b9c5-4ae3-b746-647e311bb649 20250909 1 11010000107 900000000000509007 707621010000111 900000000000549004 +41e58398-3ee1-4abb-bd09-a4e1bd6ca30c 20250909 1 11010000107 900000000000509007 943461010000111 900000000000549004 +48dde1b4-e105-4501-a198-8be5ae4dc1ba 20250909 1 11010000107 900000000000509007 66151010000111 900000000000549004 +4b989fec-ac16-496a-acc3-41f110e60c33 20250909 1 11010000107 900000000000509007 943451010000114 900000000000548007 +4bf4b364-3e90-47f4-a8ec-7a691fb8d52c 20250909 1 11010000107 900000000000509007 5088631010000113 900000000000549004 +4fcf72b6-891a-4e47-ad0b-eb2d9e38bea4 20250909 1 11010000107 900000000000509007 11010000111 900000000000548007 +518452a7-c3b4-4fb4-affa-ea6bd18ce082 20250909 1 11010000107 900000000000509007 472541010000118 900000000000549004 +53a2953b-0810-4d9b-b8f0-cabfdb15f9d2 20250909 1 11010000107 900000000000509007 497681010000116 900000000000549004 +56d08c34-2884-42d8-8b59-fb8ad96268f6 20250909 1 11010000107 900000000000509007 2220541010000115 900000000000548007 +58d782d0-abfc-4b30-8279-df7ba5401887 20250909 0 11010000107 900000000000509007 2311421010000119 900000000000549004 +5c02ec2c-3083-41f7-a444-45d980b7a564 20250909 1 11010000107 900000000000509007 404601010000118 900000000000549004 +6719655a-f976-4775-affb-919f6ded5da7 20250909 0 11010000107 900000000000509007 2603531010000114 900000000000549004 +691bafe6-818e-4780-be9c-887f147880f5 20250909 1 11010000107 900000000000509007 497671010000119 900000000000548007 +6c7d8f1c-3ce7-4f08-8832-73fc4fddd974 20250909 1 11010000107 900000000000509007 2422571010000114 900000000000549004 +6fb3b19c-a35d-4e81-868a-482696d54f11 20250909 0 11010000107 900000000000509007 2220561010000116 900000000000549004 +70c5a9c5-fc11-40ca-b4a2-82debe430741 20250909 1 11010000107 900000000000509007 404591010000112 900000000000548007 +735fb6c7-a002-42a3-a88d-bb57d9e32481 20250909 1 11010000107 900000000000509007 472551010000116 900000000000548007 +76470394-4e44-4cd7-9b93-d294394bcca9 20250909 1 11010000107 900000000000509007 66161010000113 900000000000548007 +7dcd9eb8-fb1f-4656-9cbf-b10defc1b884 20250909 1 11010000107 900000000000509007 404571010000111 900000000000548007 +7ff5473c-4f58-4f50-a643-45b37a607fd1 20250909 1 11010000107 900000000000509007 497651010000110 900000000000548007 +80af234e-cff6-45dd-a2ac-24cf2835e31e 20250909 1 11010000107 900000000000509007 472561010000119 900000000000549004 +83217d80-f472-47bf-9423-4785560dd6bc 20250909 1 11010000107 900000000000509007 155421010000115 900000000000549004 +8ea21101-0fbf-4414-ba72-fa69febcbece 20250909 1 11010000107 900000000000509007 66141010000114 900000000000548007 +916792d6-f369-4378-949b-b871c0208563 20250909 0 11010000107 900000000000509007 3134521010000113 900000000000549004 +91c1a50e-d8e5-40fe-80c3-8f158baaf24f 20250909 0 11010000107 900000000000509007 2524461010000116 900000000000549004 +920974be-ca01-4d14-bc33-029512528709 20250909 1 11010000107 900000000000509007 178921010000115 900000000000549004 +93e48c9e-caf9-45f6-a627-aafd94eaca7f 20250909 1 11010000107 900000000000509007 2220551010000118 900000000000548007 +94f0bdc0-8631-4582-b1b4-60239bda88cf 20250909 1 11010000107 900000000000509007 4709811010000118 900000000000549004 +9abe772d-4b77-455a-98ac-dc469a0aa573 20250909 1 11010000107 900000000000509007 827301010000117 900000000000549004 +9b3ae54e-6f71-486c-80ce-07bb5a53480a 20250909 1 11010000107 900000000000509007 827291010000118 900000000000548007 +9ec05146-9edd-46a1-b508-a3a722ca3922 20250909 1 11010000107 900000000000509007 472531010000114 900000000000548007 +a5becb22-5bcc-4ac2-8aa5-6b2081d6c031 20250909 0 11010000107 900000000000509007 3131041010000111 900000000000549004 +a8ebf22f-1558-4ce7-9c60-6e9b8966e7da 20250909 1 11010000107 900000000000509007 178901010000111 900000000000549004 +aafacdb6-a94a-451e-bef8-437821a5068e 20250909 1 11010000107 900000000000509007 5090571010000113 900000000000549004 +adc85a8b-df28-4e15-bf4f-3e6ffc6859f9 20250909 1 11010000107 900000000000509007 4709761010000111 900000000000549004 +b6e96eaa-4172-4d7b-88e2-63c7cc26439b 20250909 1 11010000107 900000000000509007 465061010000114 900000000000549004 +b8e89a0c-3f53-483f-a394-6f638484c230 20250909 1 11010000107 900000000000509007 4974561010000119 900000000000549004 +bc2250b5-8404-478a-8fa3-0e6cf337a320 20250909 1 11010000107 900000000000509007 497661010000112 900000000000549004 +bdf6c47a-efc8-4ee8-9d92-0686e176f62f 20250909 1 11010000107 900000000000509007 4600071010000116 900000000000549004 +be6992be-fbf3-460f-a012-8f1b9edb3883 20250909 0 11010000107 900000000000509007 2966871010000117 900000000000549004 +c456f0a8-2187-40e6-9d38-6f840f3373be 20250909 1 11010000107 900000000000509007 2422591010000110 900000000000548007 +c4ce0642-3010-4074-946c-870a6a3b873e 20250909 0 11010000107 900000000000509007 2422601010000116 900000000000549004 +c74fb0c6-b228-47e9-8a2c-d313548aaedc 20250909 1 11010000107 900000000000509007 465081010000118 900000000000549004 +c91d8503-19eb-4839-b71a-a38a73a25240 20250909 1 11010000107 900000000000509007 178891010000112 900000000000548007 +ca7d5d60-18fa-469f-88a1-98ddd3c64ccf 20250909 1 11010000107 900000000000509007 2220531010000111 900000000000549004 +cb527564-e6c2-4ffa-a278-117b6cd1af89 20250909 1 11010000107 900000000000509007 178911010000114 900000000000548007 +d295648c-eab2-4e2f-a057-13ad5250c078 20250909 1 11010000107 900000000000509007 2220571010000114 900000000000549004 +d50556ca-a95e-45b5-9bf1-999bc27ffdac 20250909 0 11010000107 900000000000509007 3131171010000116 900000000000549004 +d5f2f387-2a25-4f55-bdd8-ccab44f1471e 20250909 1 11010000107 900000000000509007 4729221010000116 900000000000549004 +d5f41143-530e-46f9-85a1-37fbc57bfbbc 20250909 1 11010000107 900000000000509007 645451010000111 900000000000548007 +d84936b3-b97d-48e1-8fa4-7461c7cfbf3e 20250909 0 11010000107 900000000000509007 2311301010000115 900000000000549004 +d943abfd-e3d4-4546-8899-df774137cbf5 20250909 1 11010000107 900000000000509007 465071010000116 900000000000548007 +defc2b6c-010d-4f9b-8177-0f44f63d58b6 20250909 1 11010000107 900000000000509007 827311010000119 900000000000548007 +df0288b0-3caa-4cc8-baa6-9d36904139aa 20250909 1 11010000107 900000000000509007 4518001010000119 900000000000549004 +e1c45fd9-df37-4cbb-800c-670b161f5215 20250909 1 11010000107 900000000000509007 645481010000117 900000000000549004 +e25af337-d345-4329-b04a-221cfc56b906 20250909 1 11010000107 900000000000509007 707631010000114 900000000000548007 +e8bd9e62-ea54-4135-abdf-0bb5b7aa2a43 20250909 1 11010000107 900000000000509007 2422611010000118 900000000000549004 +ebe25e6a-a9dd-45fb-8387-58e1d73cfb4a 20250909 1 11010000107 900000000000509007 155411010000114 900000000000548007 +f6361d99-e977-46eb-8db5-b17d8cd265f6 20250909 1 11010000107 900000000000509007 2388501010000116 900000000000549004 +fb073fb3-a50e-44d3-9cdd-15a53f2ff00f 20250909 1 11010000107 900000000000509007 404581010000114 900000000000549004 diff --git a/tx/data/snomed/Refset/Metadata/der2_ssRefset_ModuleDependencySnapshot_LO1010000_20250909.txt b/tx/data/snomed/Refset/Metadata/der2_ssRefset_ModuleDependencySnapshot_LO1010000_20250909.txt new file mode 100644 index 00000000..9230df46 --- /dev/null +++ b/tx/data/snomed/Refset/Metadata/der2_ssRefset_ModuleDependencySnapshot_LO1010000_20250909.txt @@ -0,0 +1,4 @@ +id effectiveTime active moduleId refsetId referencedComponentId sourceEffectiveTime targetEffectiveTime +1dfaaa07-df79-5067-aacd-576aa307a0a3 20250909 1 11010000107 900000000000534007 900000000000207008 20250909 20250801 +af758b73-4ec8-540b-a79f-e887777a7d79 20250909 1 31000003106 900000000000534007 11010000107 20250909 20250909 +bc79ca54-a70b-54dc-a410-dac2d3e82650 20250909 1 11010000107 900000000000534007 900000000000012004 20250909 20250801 diff --git a/tx/data/snomed/Terminology/sct2_Concept_Snapshot_LO1010000_20250909.txt b/tx/data/snomed/Terminology/sct2_Concept_Snapshot_LO1010000_20250909.txt new file mode 100644 index 00000000..fce5c815 --- /dev/null +++ b/tx/data/snomed/Terminology/sct2_Concept_Snapshot_LO1010000_20250909.txt @@ -0,0 +1,15 @@ +id effectiveTime active moduleId definitionStatusId +11010000107 20250909 1 11010000107 900000000000074008 +119661010000102 20250909 1 11010000107 900000000000074008 +134781010000108 20250909 1 11010000107 900000000000074008 +136651010000102 20250909 1 11010000107 900000000000074008 +142931010000103 20250909 1 11010000107 900000000000074008 +179881010000103 20250909 1 11010000107 900000000000074008 +195421010000109 20250909 1 11010000107 900000000000074008 +225341010000104 20250909 1 11010000107 900000000000074008 +254381010000105 20250909 1 11010000107 900000000000074008 +30051010000102 20250909 1 11010000107 900000000000074008 +483081010000100 20250909 1 11010000107 900000000000074008 +510211010000101 20250909 1 11010000107 900000000000074008 +57371010000105 20250909 0 11010000107 900000000000074008 +63241010000109 20250909 1 11010000107 900000000000074008 diff --git a/tx/data/snomed/Terminology/sct2_Description_Snapshot-en_LO1010000_20250909.txt b/tx/data/snomed/Terminology/sct2_Description_Snapshot-en_LO1010000_20250909.txt new file mode 100644 index 00000000..cb371223 --- /dev/null +++ b/tx/data/snomed/Terminology/sct2_Description_Snapshot-en_LO1010000_20250909.txt @@ -0,0 +1,77 @@ +id effectiveTime active moduleId conceptId languageCode typeId term caseSignificanceId +11010000111 20250909 1 11010000107 11010000107 en 900000000000013009 LOINC Extension module 900000000000017005 +155411010000114 20250909 1 11010000107 57371010000105 en 900000000000003001 Mass concentration of chenodeoxycholylglycine, conjugated in serum at point in time (observable entity) 900000000000448009 +155421010000115 20250909 1 11010000107 57371010000105 en 900000000000013009 Mass concentration of chenodeoxycholylglycine, conjugated in serum at point in time 900000000000448009 +155431010000117 20250909 1 11010000107 57371010000105 en 900000000000013009 Chenodeoxycholylglycine.conjugated [Mass/volume] in Serum 900000000000017005 +155441010000113 20250909 1 11010000107 57371010000105 en 900000000000013009 Chenodeoxycholylglycine.conjugated:MCnc:Pt:Ser:Qn 900000000000017005 +178891010000112 20250909 1 11010000107 63241010000109 en 900000000000003001 Mass concentration of urea nitrogen in pleural fluid at point in time (observable entity) 900000000000448009 +178901010000111 20250909 1 11010000107 63241010000109 en 900000000000013009 Mass concentration of urea nitrogen in pleural fluid at point in time 900000000000448009 +178911010000114 20250909 1 11010000107 63241010000109 en 900000000000013009 Urea nitrogen [Mass/volume] in Pleural fluid 900000000000017005 +178921010000115 20250909 1 11010000107 63241010000109 en 900000000000013009 Urea nitrogen:MCnc:Pt:Plr fld:Qn 900000000000017005 +21010000117 20250909 1 11010000107 11010000107 en 900000000000003001 LOINC Extension module (core metadata concept) 900000000000017005 +2220531010000111 20250909 1 11010000107 483081010000100 en 900000000000013009 Mass concentration of glucose in venous blood at point in time using glucometer 900000000000448009 +2220541010000115 20250909 1 11010000107 483081010000100 en 900000000000013009 Glucose [Mass/volume] in Venous blood by Glucometer 900000000000017005 +2220551010000118 20250909 1 11010000107 483081010000100 en 900000000000003001 Mass concentration of glucose in venous blood at point in time using glucometer (observable entity) 900000000000448009 +2220561010000116 20250909 0 11010000107 483081010000100 en 900000000000013009 Glucose Glucometer (BldV) [Mass/Vol] 900000000000017005 +2220571010000114 20250909 1 11010000107 483081010000100 en 900000000000013009 Glucose:MCnc:Pt:BldV:Qn:Glucometer 900000000000017005 +2311301010000115 20250909 0 11010000107 63241010000109 en 900000000000013009 Urea nitrogen (Pleur fld) [Mass/Vol] 900000000000017005 +2311421010000119 20250909 0 11010000107 136651010000102 en 900000000000013009 Creatinine (Gast fld) [Mass/Vol] 900000000000017005 +2388501010000116 20250909 1 11010000107 57371010000105 en 900000000000013009 Chenodeoxycholylglycine.conjugated (S) [Mass/Vol] 900000000000017005 +2422571010000114 20250909 1 11010000107 510211010000101 en 900000000000013009 Mass concentration of potassium electrolyte in serum or plasma at point in time 900000000000448009 +2422581010000112 20250909 1 11010000107 510211010000101 en 900000000000013009 Potassium [Mass/volume] in Serum or Plasma 900000000000017005 +2422591010000110 20250909 1 11010000107 510211010000101 en 900000000000003001 Mass concentration of potassium electrolyte in serum or plasma at point in time (observable entity) 900000000000448009 +2422601010000116 20250909 0 11010000107 510211010000101 en 900000000000013009 Potassium [Mass/Vol] 900000000000017005 +2422611010000118 20250909 1 11010000107 510211010000101 en 900000000000013009 Potassium:MCnc:Pt:Ser/Plas:Qn 900000000000017005 +2524461010000116 20250909 0 11010000107 254381010000105 en 900000000000013009 Potassium [Moles/Vol] 900000000000017005 +2524551010000117 20250909 0 11010000107 179881010000103 en 900000000000013009 Potassium (U) [Moles/Vol] 900000000000017005 +2603531010000114 20250909 0 11010000107 142931010000103 en 900000000000013009 Carboxyhemoglobin (Bld) [Mass/Vol] 900000000000017005 +2966871010000117 20250909 0 11010000107 195421010000109 en 900000000000013009 Potassium (Bld) [Moles/Vol] 900000000000017005 +3131041010000111 20250909 0 11010000107 119661010000102 en 900000000000013009 C peptide 2 Hr post meal [Mass/Vol] 900000000000017005 +3131171010000116 20250909 0 11010000107 225341010000104 en 900000000000013009 Glucagon 2 Hr post meal [Mass/Vol] 900000000000017005 +3134521010000113 20250909 0 11010000107 134781010000108 en 900000000000013009 25-hydroxyvitamin D3 (BldC) [Mass/Vol] 900000000000017005 +404571010000111 20250909 1 11010000107 119661010000102 en 900000000000003001 Mass concentration of C-peptide in serum or plasma at point in time two hours after a meal (observable entity) 900000000000020002 +404581010000114 20250909 1 11010000107 119661010000102 en 900000000000013009 Mass concentration of C-peptide in serum or plasma at point in time two hours after a meal 900000000000020002 +404591010000112 20250909 1 11010000107 119661010000102 en 900000000000013009 C peptide [Mass/volume] in Serum or Plasma --2 hours post meal 900000000000017005 +404601010000118 20250909 1 11010000107 119661010000102 en 900000000000013009 C peptide^2H post meal:MCnc:Pt:Ser/Plas:Qn 900000000000017005 +4518001010000119 20250909 1 11010000107 483081010000100 en 900000000000013009 Glucose BldV Glucomtr-mCnc 900000000000017005 +4600071010000116 20250909 1 11010000107 63241010000109 en 900000000000013009 Urea nit Plr-mCnc 900000000000017005 +4600151010000111 20250909 1 11010000107 136651010000102 en 900000000000013009 Creat Gast-mCnc 900000000000017005 +465051010000112 20250909 1 11010000107 134781010000108 en 900000000000003001 Mass concentration of calcifediol in capillary blood at point in time (observable entity) 900000000000448009 +465061010000114 20250909 1 11010000107 134781010000108 en 900000000000013009 Mass concentration of calcifediol in capillary blood at point in time 900000000000448009 +465071010000116 20250909 1 11010000107 134781010000108 en 900000000000013009 25-hydroxyvitamin D3 [Mass/volume] in Capillary blood 900000000000017005 +465081010000118 20250909 1 11010000107 134781010000108 en 900000000000013009 Calcidiol:MCnc:Pt:BldC:Qn 900000000000017005 +4670061010000117 20250909 1 11010000107 510211010000101 en 900000000000013009 Potassium SerPl-mCnc 900000000000017005 +4709761010000111 20250909 1 11010000107 254381010000105 en 900000000000013009 Potassium SerPl-sCnc 900000000000017005 +4709811010000118 20250909 1 11010000107 179881010000103 en 900000000000013009 Potassium Ur-sCnc 900000000000017005 +472531010000114 20250909 1 11010000107 136651010000102 en 900000000000003001 Mass concentration of creatinine in gastric fluid at point in time (observable entity) 900000000000448009 +472541010000118 20250909 1 11010000107 136651010000102 en 900000000000013009 Mass concentration of creatinine in gastric fluid at point in time 900000000000448009 +472551010000116 20250909 1 11010000107 136651010000102 en 900000000000013009 Creatinine [Mass/volume] in Gastric fluid 900000000000017005 +472561010000119 20250909 1 11010000107 136651010000102 en 900000000000013009 Creatinine:MCnc:Pt:Gast fld:Qn 900000000000017005 +4729221010000116 20250909 1 11010000107 142931010000103 en 900000000000013009 COHgb Bld-mCnc 900000000000017005 +4974561010000119 20250909 1 11010000107 195421010000109 en 900000000000013009 Potassium Bld-sCnc 900000000000017005 +497651010000110 20250909 1 11010000107 142931010000103 en 900000000000003001 Mass concentration of carboxyhemoglobin in blood at point in time (observable entity) 900000000000448009 +497661010000112 20250909 1 11010000107 142931010000103 en 900000000000013009 Mass concentration of carboxyhemoglobin in blood at point in time 900000000000448009 +497671010000119 20250909 1 11010000107 142931010000103 en 900000000000013009 Carboxyhemoglobin [Mass/volume] in Blood 900000000000017005 +497681010000116 20250909 1 11010000107 142931010000103 en 900000000000013009 Carboxyhemoglobin:MCnc:Pt:Bld:Qn 900000000000017005 +5088631010000113 20250909 1 11010000107 119661010000102 en 900000000000013009 C peptide 2h p meal SerPl-mCnc 900000000000017005 +5088731010000118 20250909 1 11010000107 225341010000104 en 900000000000013009 Glucagon 2h p meal SerPl-mCnc 900000000000017005 +5090571010000113 20250909 1 11010000107 134781010000108 en 900000000000013009 25(OH)D3 BldC-mCnc 900000000000017005 +645451010000111 20250909 1 11010000107 179881010000103 en 900000000000003001 Substance concentration of potassium electrolyte in urine at point in time (observable entity) 900000000000448009 +645461010000113 20250909 1 11010000107 179881010000103 en 900000000000013009 Substance concentration of potassium electrolyte in urine at point in time 900000000000448009 +645471010000115 20250909 1 11010000107 179881010000103 en 900000000000013009 Potassium [Moles/volume] in Urine 900000000000017005 +645481010000117 20250909 1 11010000107 179881010000103 en 900000000000013009 Potassium:SCnc:Pt:Urine:Qn 900000000000017005 +66141010000114 20250909 1 11010000107 30051010000102 en 900000000000013009 LOINC code ID 900000000000017005 +66151010000111 20250909 1 11010000107 30051010000102 en 900000000000013009 LOINC code identifier 900000000000017005 +66161010000113 20250909 1 11010000107 30051010000102 en 900000000000003001 LOINC code identifier (core metadata concept) 900000000000017005 +707611010000117 20250909 1 11010000107 195421010000109 en 900000000000003001 Substance concentration of potassium electrolyte in blood at point in time (observable entity) 900000000000448009 +707621010000111 20250909 1 11010000107 195421010000109 en 900000000000013009 Substance concentration of potassium electrolyte in blood at point in time 900000000000448009 +707631010000114 20250909 1 11010000107 195421010000109 en 900000000000013009 Potassium [Moles/volume] in Blood 900000000000017005 +707641010000118 20250909 1 11010000107 195421010000109 en 900000000000013009 Potassium:SCnc:Pt:Bld:Qn 900000000000017005 +827291010000118 20250909 1 11010000107 225341010000104 en 900000000000003001 Mass concentration of glucagon in serum or plasma at point in time two hours after a meal (observable entity) 900000000000448009 +827301010000117 20250909 1 11010000107 225341010000104 en 900000000000013009 Mass concentration of glucagon in serum or plasma at point in time two hours after a meal 900000000000448009 +827311010000119 20250909 1 11010000107 225341010000104 en 900000000000013009 Glucagon [Mass/volume] in Serum or Plasma --2 hours post meal 900000000000017005 +827321010000113 20250909 1 11010000107 225341010000104 en 900000000000013009 Glucagon^2H post meal:MCnc:Pt:Ser/Plas:Qn 900000000000017005 +943451010000114 20250909 1 11010000107 254381010000105 en 900000000000003001 Substance concentration of potassium electrolyte in serum or plasma at point in time (observable entity) 900000000000448009 +943461010000111 20250909 1 11010000107 254381010000105 en 900000000000013009 Substance concentration of potassium electrolyte in serum or plasma at point in time 900000000000448009 +943471010000118 20250909 1 11010000107 254381010000105 en 900000000000013009 Potassium [Moles/volume] in Serum or Plasma 900000000000017005 +943481010000115 20250909 1 11010000107 254381010000105 en 900000000000013009 Potassium:SCnc:Pt:Ser/Plas:Qn 900000000000017005 diff --git a/tx/data/snomed/Terminology/sct2_Identifier_Snapshot_LO1010000_20250909.txt b/tx/data/snomed/Terminology/sct2_Identifier_Snapshot_LO1010000_20250909.txt new file mode 100644 index 00000000..ff226195 --- /dev/null +++ b/tx/data/snomed/Terminology/sct2_Identifier_Snapshot_LO1010000_20250909.txt @@ -0,0 +1,13 @@ +alternateIdentifier effectiveTime active moduleId identifierSchemeId referencedComponentId +104597-0 20250909 1 11010000107 30051010000102 483081010000100 +14394-1 20250909 1 11010000107 30051010000102 63241010000109 +14402-2 20250909 1 11010000107 30051010000102 136651010000102 +2067-7 20250909 1 11010000107 30051010000102 57371010000105 +22760-3 20250909 1 11010000107 30051010000102 510211010000101 +2823-3 20250909 1 11010000107 30051010000102 254381010000105 +2828-2 20250909 1 11010000107 30051010000102 179881010000103 +31157-1 20250909 1 11010000107 30051010000102 142931010000103 +6298-4 20250909 1 11010000107 30051010000102 195421010000109 +95084-0 20250909 1 11010000107 30051010000102 119661010000102 +95095-6 20250909 1 11010000107 30051010000102 225341010000104 +95594-8 20250909 1 11010000107 30051010000102 134781010000108 diff --git a/tx/data/snomed/Terminology/sct2_Relationship_Snapshot_LO1010000_20250909.txt b/tx/data/snomed/Terminology/sct2_Relationship_Snapshot_LO1010000_20250909.txt new file mode 100644 index 00000000..47464ba2 --- /dev/null +++ b/tx/data/snomed/Terminology/sct2_Relationship_Snapshot_LO1010000_20250909.txt @@ -0,0 +1,14 @@ +id effectiveTime active moduleId sourceId destinationId relationshipGroup typeId characteristicTypeId modifierId +990000011010000122 20250909 1 11010000107 254381010000105 11953005 0 116680003 900000000000011006 900000000000451002 +990000021010000127 20250909 1 11010000107 510211010000101 11953005 0 116680003 900000000000011006 900000000000451002 +990000031010000129 20250909 1 11010000107 195421010000109 11953005 0 116680003 900000000000011006 900000000000451002 +990000041010000121 20250909 1 11010000107 179881010000103 11953005 0 116680003 900000000000011006 900000000000451002 +990000051010000123 20250909 1 11010000107 136651010000102 11953005 0 116680003 900000000000011006 900000000000451002 +990000061010000120 20250909 1 11010000107 63241010000109 11953005 0 116680003 900000000000011006 900000000000451002 +990000071010000126 20250909 1 11010000107 483081010000100 363787002 0 116680003 900000000000011006 900000000000451002 +990000081010000128 20250909 1 11010000107 119661010000102 363787002 0 116680003 900000000000011006 900000000000451002 +990000091010000125 20250909 1 11010000107 225341010000104 363787002 0 116680003 900000000000011006 900000000000451002 +990000101010000123 20250909 1 11010000107 142931010000103 363787002 0 116680003 900000000000011006 900000000000451002 +990000111010000121 20250909 1 11010000107 134781010000108 363787002 0 116680003 900000000000011006 900000000000451002 +990000121010000126 20250909 1 11010000107 11010000107 900000000000443000 0 116680003 900000000000011006 900000000000451002 +990000131010000128 20250909 1 11010000107 30051010000102 900000000000454005 0 116680003 900000000000011006 900000000000451002 diff --git a/tx/data/snomed/Terminology/sct2_sRefset_OWLExpressionSnapshot_LO1010000_20250909.txt b/tx/data/snomed/Terminology/sct2_sRefset_OWLExpressionSnapshot_LO1010000_20250909.txt new file mode 100644 index 00000000..3e58c3ff --- /dev/null +++ b/tx/data/snomed/Terminology/sct2_sRefset_OWLExpressionSnapshot_LO1010000_20250909.txt @@ -0,0 +1,14 @@ +id effectiveTime active moduleId refsetId referencedComponentId owlExpression +0b7f8712-97db-59c5-afce-92e956b0e1df 20250909 1 11010000107 733073007 510211010000101 SubClassOf(:510211010000101 :11953005) +155132d4-9bff-5192-a784-204428b9062f 20250909 1 11010000107 733073007 11010000107 SubClassOf(:11010000107 :900000000000443000) +16d24943-df9d-5fe3-a9c3-4ba0ba8367bf 20250909 1 11010000107 733073007 30051010000102 SubClassOf(:30051010000102 :900000000000454005) +4fd7d652-e9bf-5343-ade8-192af8567ff0 20250909 1 11010000107 733073007 179881010000103 SubClassOf(:179881010000103 :11953005) +818bb4c3-9498-5a65-af60-021138ddea9c 20250909 1 11010000107 733073007 119661010000102 SubClassOf(:119661010000102 :363787002) +b7d20f3b-e641-51f2-a281-df4c336702ab 20250909 1 11010000107 733073007 195421010000109 SubClassOf(:195421010000109 :11953005) +b8de987d-4d19-5c4c-a917-9c278d7921cd 20250909 1 11010000107 733073007 134781010000108 SubClassOf(:134781010000108 :363787002) +d5a5af20-f5f5-5520-af46-cfb75156288c 20250909 1 11010000107 733073007 142931010000103 SubClassOf(:142931010000103 :363787002) +e7e98c01-7671-5c81-a508-ab513b8c88b8 20250909 1 11010000107 733073007 63241010000109 SubClassOf(:63241010000109 :11953005) +e98a2007-f4df-52b6-a768-e2d9ceff27a2 20250909 1 11010000107 733073007 136651010000102 SubClassOf(:136651010000102 :11953005) +ee26b49a-9b20-5c1c-a77a-4d8540306a60 20250909 1 11010000107 733073007 254381010000105 SubClassOf(:254381010000105 :11953005) +f07c9881-a58b-5aef-a241-0623cc8f7bb7 20250909 1 11010000107 733073007 483081010000100 SubClassOf(:483081010000100 :363787002) +f63998e8-e060-54a9-af29-72fedcd8ba09 20250909 1 11010000107 733073007 225341010000104 SubClassOf(:225341010000104 :363787002) diff --git a/tx/fixtures/test-cases.yml b/tx/fixtures/test-cases.yml index 61de3d66..2daaec9c 100644 --- a/tx/fixtures/test-cases.yml +++ b/tx/fixtures/test-cases.yml @@ -11,9 +11,9 @@ sources: - internal:hgvs - internal:urls - ucum:tx/data/ucum-essence.xml - - loinc!:Loinc_2.82.db + - loinc!:Loinc_2.83.db - snomed!:sct_intl_20250201.cache - - snomed:sct_test_20250909c.cache + - snomed:sct_test_20250909d.cache - omop:omop_v20250227.db - icd11:icd11-2026-01-b.db - npm:hl7.terminology=tho diff --git a/tx/importers/csv-records.js b/tx/importers/csv-records.js new file mode 100644 index 00000000..e7d4384b --- /dev/null +++ b/tx/importers/csv-records.js @@ -0,0 +1,35 @@ +const fs = require('fs'); +const readline = require('readline'); + +/** + * Yields the records of a CSV file one at a time, as text. + * + * A quoted field can contain a line break (LOINC 2.83's ComponentHierarchyBySystem.csv has one + * in a CODE_TEXT), so a record that ends inside quotes is joined to the next line until the + * quotes balance. An escaped quote ("") counts twice, so it doesn't upset the count. + */ +async function* csvRecords(filePath) { + const rl = readline.createInterface({ + input: fs.createReadStream(filePath), + crlfDelay: Infinity + }); + + let pending = null; + let quotes = 0; + for await (const line of rl) { + pending = pending === null ? line : pending + '\n' + line; + for (let i = 0; i < line.length; i++) { + if (line.charCodeAt(i) === 34) quotes++; + } + if (quotes % 2 === 0) { + yield pending; + pending = null; + quotes = 0; + } + } + if (pending !== null) { + yield pending; + } +} + +module.exports = { csvRecords }; diff --git a/tx/importers/import-loinc.module.js b/tx/importers/import-loinc.module.js index 0932f66d..c2eef0c5 100644 --- a/tx/importers/import-loinc.module.js +++ b/tx/importers/import-loinc.module.js @@ -3,6 +3,7 @@ const fs = require('fs'); const sqlite3 = require('sqlite3').verbose(); const path = require('path'); const readline = require('readline'); +const { csvRecords } = require('./csv-records'); const chalk = require('chalk'); class LoincModule extends BaseTerminologyModule { @@ -291,7 +292,10 @@ class LoincModule extends BaseTerminologyModule { // Translations are a different case: a main-only import is a legitimate thing to want, // and their absence costs designations, not correctness const optionalFiles = [ - 'AccessoryFiles/LinguisticVariants/LinguisticVariants.csv' + 'AccessoryFiles/LinguisticVariants/LinguisticVariants.csv', + // the external code mappings: their absence costs maps to other code systems, nothing else + 'AccessoryFiles/PartFile/PartRelatedCodeMapping.csv', + 'AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv' ]; const warnings = []; @@ -428,6 +432,11 @@ class LoincModule extends BaseTerminologyModule { 'CREATE INDEX IF NOT EXISTS idx_descriptions_type ON Descriptions(DescriptionTypeKey)', 'CREATE INDEX IF NOT EXISTS idx_descriptions_code_type ON Descriptions(CodeKey, DescriptionTypeKey)', + // External mapping indexes + 'CREATE INDEX IF NOT EXISTS idx_externalcodes_code ON ExternalCodes(System, Code)', + 'CREATE INDEX IF NOT EXISTS idx_externalmappings_code ON ExternalMappings(CodeKey)', + 'CREATE INDEX IF NOT EXISTS idx_externalmappings_target ON ExternalMappings(ExternalCodeKey)', + // Closure table indexes 'CREATE INDEX IF NOT EXISTS idx_closure_ancestor ON Closure(AncestorKey)', 'CREATE INDEX IF NOT EXISTS idx_closure_descendent ON Closure(DescendentKey)' @@ -506,7 +515,9 @@ class LoincDataMigratorWithProgress { 'AccessoryFiles/AnswerFile/AnswerList.csv', 'AccessoryFiles/PartFile/LoincPartLink_Primary.csv', 'AccessoryFiles/AnswerFile/LoincAnswerListLink.csv', - 'AccessoryFiles/ComponentHierarchyBySystem/ComponentHierarchyBySystem.csv' + 'AccessoryFiles/ComponentHierarchyBySystem/ComponentHierarchyBySystem.csv', + 'AccessoryFiles/PartFile/PartRelatedCodeMapping.csv', + 'AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv' ]; // Count main files @@ -599,6 +610,10 @@ class LoincDataMigrator { this.props = new Map(); this.propValues = new Map(); this.partNames = new Map(); + this.extCodeKey = 0; + this.extCodes = new Map(); // system|version|code -> ExternalCodeKey + this.extMapKey = 0; + this.extMaps = new Set(); // codeKey|ExternalCodeKey // Create tables and initial data await this.createTables(db, version, options.verbose); @@ -631,6 +646,10 @@ class LoincDataMigrator { this.updateProgress(0, 'List Links'); await this.processListLinks(db, sourceDir, 8, options); + this.updateProgress(0, 'External Mappings'); + await this.processPartMappings(db, sourceDir, 9, options); + await this.processIeeeMappings(db, sourceDir, 9, options); + this.updateProgress(0, 'Hierarchy'); await this.processHierarchy(db, sourceDir, 9, options); @@ -719,6 +738,21 @@ class LoincDataMigrator { DescriptionTypeKey INTEGER NOT NULL, Value TEXT NOT NULL )`, + // Codes in other code systems that LOINC codes map to (one row per system|version|code) + `CREATE TABLE ExternalCodes ( + ExternalCodeKey INTEGER NOT NULL PRIMARY KEY, + System TEXT NOT NULL, + Version TEXT NULL, + Code TEXT NOT NULL, + Display TEXT NULL + )`, + // LOINC code (Codes.CodeKey) -> ExternalCodes, with a ConceptMap equivalence (R4 codes) + `CREATE TABLE ExternalMappings ( + ExternalMappingKey INTEGER NOT NULL PRIMARY KEY, + CodeKey INTEGER NOT NULL, + ExternalCodeKey INTEGER NOT NULL, + Equivalence TEXT NOT NULL + )`, `CREATE TABLE Closure ( AncestorKey INTEGER NOT NULL, DescendentKey INTEGER NOT NULL, @@ -843,10 +877,7 @@ class LoincDataMigrator { return; } - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; for await (const line of rl) { @@ -872,10 +903,7 @@ class LoincDataMigrator { if (options.verbose) console.log('Processing Parts...'); const filePath = path.join(sourceDir, 'AccessoryFiles/PartFile/Part.csv'); - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; let processedCount = 0; @@ -927,10 +955,7 @@ class LoincDataMigrator { if (options.verbose) console.log('Processing Main Codes...'); const filePath = path.join(sourceDir, 'LoincTable/Loinc.csv'); - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; let processedCount = 0; @@ -1007,10 +1032,7 @@ class LoincDataMigrator { if (options.verbose) console.log('Processing Consumer Names...'); - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; let processedCount = 0; @@ -1045,10 +1067,7 @@ class LoincDataMigrator { if (options.verbose) console.log('Processing Answer Lists...'); - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; let processedCount = 0; @@ -1058,7 +1077,7 @@ class LoincDataMigrator { lineCount++; if (lineCount === 1) continue; - const items = csvSplit(line, 11); + const items = csvSplit(line, 15); if (items.length < 11) continue; await this.processListItem(db, items, currentList); @@ -1111,6 +1130,111 @@ class LoincDataMigrator { this.addRelationship(db, listCodeKey, answerCodeKey, this.rels.get('Answer')); this.addRelationship(db, answerCodeKey, listCodeKey, this.rels.get('AnswerList')); + + // ExtCodeId (11), ExtCodeDisplayName (12), ExtCodeSystem (13), ExtCodeSystemVersion (14). + // The file carries no equivalence for these; an answer and its external code are the same thing + if (items[11] && items[13]) { + this.addExternalMapping(db, answerCodeKey, items[13], items[14], items[11], items[12], 'equivalent'); + } + } + + async processPartMappings(db, sourceDir, step, options) { + const filePath = path.join(sourceDir, 'AccessoryFiles/PartFile/PartRelatedCodeMapping.csv'); + if (!fs.existsSync(filePath)) { + if (options.verbose) console.warn(`Part mapping file not found: ${filePath}`); + return; + } + + if (options.verbose) console.log('Processing Part Mappings...'); + + const rl = csvRecords(filePath); + + // PartNumber, PartName, PartTypeName, ExtCodeId, ExtCodeDisplayName, ExtCodeSystem, + // Equivalence, ContentOrigin, ExtCodeSystemVersion, ExtCodeSystemCopyrightNotice + let lineCount = 0; + let processedCount = 0; + for await (const line of rl) { + lineCount++; + if (lineCount === 1) continue; + + const items = csvSplit(line, 10); + const partCode = items[0]; + const system = items[5]; + // RadLex mappings are not simple code-to-code maps; not handled yet + if (!items[3] || !system || system === 'http://www.radlex.org' || !this.codes.has(partCode)) continue; + + this.addExternalMapping(db, this.codes.get(partCode).key, system, items[8], items[3], items[4], items[6] || 'equivalent'); + processedCount++; + + if (processedCount % 100 === 0) { + this.updateProgress(100); + } + } + + const remaining = processedCount % 100; + if (remaining > 0) { + this.updateProgress(remaining); + } + } + + async processIeeeMappings(db, sourceDir, step, options) { + const filePath = path.join(sourceDir, 'AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv'); + if (!fs.existsSync(filePath)) { + if (options.verbose) console.warn(`IEEE mapping file not found: ${filePath}`); + return; + } + + if (options.verbose) console.log('Processing IEEE Mappings...'); + + const rl = csvRecords(filePath); + + // LOINC_NUM, LOINC_LONG_COMMON_NAME, IEEE_CF_CODE10, IEEE_REFID, EQUIVALENCE + // The code is the CF_CODE10 (as used in FHIR, e.g. 150456); the REFID is its display. + let lineCount = 0; + let processedCount = 0; + for await (const line of rl) { + lineCount++; + if (lineCount === 1) continue; + + const items = csvSplit(line, 5); + const loincCode = items[0]; + if (!items[2] || !this.codes.has(loincCode)) continue; + + this.addExternalMapping(db, this.codes.get(loincCode).key, 'urn:iso:std:iso:11073:10101', null, + items[2], items[3], ieeeEquivalence(items[4])); + processedCount++; + + if (processedCount % 100 === 0) { + this.updateProgress(100); + } + } + + const remaining = processedCount % 100; + if (remaining > 0) { + this.updateProgress(remaining); + } + } + + addExternalMapping(db, codeKey, system, version, code, display, equivalence) { + system = normaliseExternalSystem(system); + version = version || null; + const targetId = system + '|' + (version || '') + '|' + code; + let extKey = this.extCodes.get(targetId); + if (!extKey) { + this.extCodeKey++; + extKey = this.extCodeKey; + this.extCodes.set(targetId, extKey); + db.run('INSERT INTO ExternalCodes (ExternalCodeKey, System, Version, Code, Display) VALUES (?, ?, ?, ?, ?)', + [extKey, system, version, code, display || null]); + } + + // the same answer appears once per answer list it's in + const mapId = codeKey + '|' + extKey; + if (this.extMaps.has(mapId)) return; + this.extMaps.add(mapId); + this.extMapKey++; + db.run('INSERT INTO ExternalMappings (ExternalMappingKey, CodeKey, ExternalCodeKey, Equivalence) VALUES (?, ?, ?, ?)', + [this.extMapKey, codeKey, extKey, equivalence]); } async processPartLinks(db, sourceDir, step, options) { @@ -1122,10 +1246,7 @@ class LoincDataMigrator { if (options.verbose) console.log('Processing Part Links...'); - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; let processedCount = 0; @@ -1171,10 +1292,7 @@ class LoincDataMigrator { if (options.verbose) console.log('Processing List Links...'); - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; let processedCount = 0; @@ -1227,10 +1345,7 @@ class LoincDataMigrator { if (options.verbose) console.log('Processing Hierarchy...'); - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; let processedCount = 0; @@ -1258,7 +1373,7 @@ class LoincDataMigrator { async processHierarchyItem(db, items) { const pathCode = items[3]; const parentPath = items[2]; - const description = items[4]; + const description = items[4].trim(); if (!this.codes.has(pathCode)) { this.codeKey++; @@ -1339,10 +1454,7 @@ class LoincDataMigrator { return; } - const rl = readline.createInterface({ - input: fs.createReadStream(filePath), - crlfDelay: Infinity - }); + const rl = csvRecords(filePath); let lineCount = 0; let processedCount = 0; @@ -1488,6 +1600,29 @@ function adjustPropName(s) { throw new Error(`Unknown Property Name: ${s}`); } +// LOINC names some code systems with something other than their FHIR URI. The others not yet +// known ('CMS LCDS', 'Unicode') are left as LOINC has them +const EXTERNAL_SYSTEM_URIS = { + 'CMS/HCPCS': 'http://www.cms.gov/Medicare/Coding/HCPCSReleaseCodeSets', + 'https://www.cdc.gov/phin': 'urn:oid:2.16.840.1.113883.6.238' // CDC Race & Ethnicity +}; + +function normaliseExternalSystem(system) { + return EXTERNAL_SYSTEM_URIS[system] || system; +} + +// The IEEE table states equivalence from the LOINC side ('broader' = the LOINC code is broader, +// e.g. Glucose in Serum or Plasma -> MDC_CONC_GLU_VENOUS_PLASMA). ConceptMap equivalence is stated +// from the target side, so broader/narrower swap to narrower/wider. +function ieeeEquivalence(s) { + switch (s) { + case 'broader': return 'narrower'; + case 'narrower': return 'wider'; + case '': return 'equivalent'; + default: return s; + } +} + function descClassType(s) { const types = { '1': 'Laboratory class', @@ -1539,5 +1674,6 @@ function removeQuotes(str) { module.exports = { LoincModule, - LoincDataMigrator + LoincDataMigrator, + normaliseExternalSystem }; \ No newline at end of file diff --git a/tx/importers/import-sct.module.js b/tx/importers/import-sct.module.js index 215dcab7..71de86eb 100644 --- a/tx/importers/import-sct.module.js +++ b/tx/importers/import-sct.module.js @@ -1136,6 +1136,9 @@ class SnomedImporter { // Step 10: Read relationships await this.readRelationships(); + // Step 10a: Read alternate identifiers (e.g. the LOINC codes of the LOINC Extension) + await this.readIdentifiers(); + // Step 11: Link concepts this.linkConcepts(); @@ -1165,6 +1168,7 @@ class SnomedImporter { concepts: [], descriptions: [], relationships: [], + identifiers: [], refsetDirectories: [] }; @@ -1218,6 +1222,8 @@ class SnomedImporter { } else if (firstLine.startsWith('id\teffectiveTime\tactive\tmoduleId\tsourceId\tdestinationId\trelationshipGroup\ttypeId\tcharacteristicTypeId\tmodifierId') && !filePath.includes('StatedRelationship')) { files.relationships.push(filePath); + } else if (files.identifiers && firstLine.startsWith('alternateIdentifier\teffectiveTime\tactive\tmoduleId\tidentifierSchemeId\treferencedComponentId')) { + files.identifiers.push(filePath); } } catch (error) { console.log(`DEBUG: Error reading file ${filePath}: ${error.message}`); @@ -1726,6 +1732,56 @@ class SnomedImporter { return /^\d+$/.test(str); } + /** + * Read the alternate identifier files (sct2_Identifier). Each active row gives a + * concept a code in another scheme - the LOINC Extension, for instance, gives each + * of its concepts its LOINC code (scheme 30051010000102 |LOINC code identifier|). + * Kept as [scheme, module, code, conceptId] for the concepts in this edition; where + * a row appears more than once (Full files, or base + extension) the latest wins. + */ + async readIdentifiers() { + const rows = new Map(); // scheme|code|concept -> {effectiveTime, active, row} + for (const file of this.files.identifiers || []) { + const rl = readline.createInterface({ + input: fs.createReadStream(file), + crlfDelay: Infinity + }); + let lineCount = 0; + for await (const line of rl) { + lineCount++; + if (lineCount === 1 || !line) continue; + const parts = line.split('\t'); + if (parts.length < 6) continue; + const [code, effectiveTime, active, moduleId, schemeId, conceptId] = parts.map(p => p.trim()); + const key = schemeId + '|' + code + '|' + conceptId; + const prior = rows.get(key); + if (!prior || prior.effectiveTime <= effectiveTime) { + rows.set(key, { effectiveTime, active: active === '1', row: [schemeId, moduleId, code, conceptId] }); + } + } + } + this.identifiers = []; + let missing = 0; + for (const r of rows.values()) { + if (!r.active) continue; + let known = false; + try { + known = this.conceptMap.has(BigInt(r.row[3])); + } catch { + known = false; + } + if (known) { + this.identifiers.push(r.row); + } else { + missing++; + } + } + this.identifiers.sort((a, b) => (a.join('|') < b.join('|') ? -1 : 1)); + if (this.identifiers.length > 0 || missing > 0) { + console.log(`Alternate identifiers: ${this.identifiers.length}${missing ? ` (${missing} for concepts not in this edition, ignored)` : ''}`); + } + } + async readRelationships() { // Estimate total lines let totalLines = 0; @@ -2766,8 +2822,12 @@ class SnomedImporter { progressBar?.update(50); + const identifiers = this.identifiers || []; await writer.writeCache({ - version: '17', // Current version + // 18 = 17 plus the alternate identifiers. Only used when there are some, so that + // caches without them stay readable by servers that only know 17 + version: identifiers.length > 0 ? '18' : '17', + identifiers: identifiers, versionUri: this.isTesting ? this.config.uri.replace("/sct/", "/xsct/") : this.config.uri, versionDate: this.extractDateFromUri(this.config.uri), @@ -2854,6 +2914,11 @@ class SnomedCacheWriter { } buffers.push(this.writeInteger(data.defaultLanguage)); + if (data.version === '18') { + // alternate identifiers, as JSON: [[scheme, module, code, conceptId], ...] + buffers.push(this.writeBytes(Buffer.from(JSON.stringify(data.identifiers || []), 'utf8'))); + } + // Write to file const finalBuffer = Buffer.concat(buffers); await fs.promises.writeFile(this.filePath, finalBuffer); diff --git a/tx/importers/subset-loinc.module.js b/tx/importers/subset-loinc.module.js index 35997296..3e78b5e9 100644 --- a/tx/importers/subset-loinc.module.js +++ b/tx/importers/subset-loinc.module.js @@ -2,6 +2,7 @@ const { BaseTerminologyModule } = require('./tx-import-base'); const fs = require('fs'); const path = require('path'); const readline = require('readline'); +const { csvRecords } = require('./csv-records'); const chalk = require('chalk'); class LoincSubsetModule extends BaseTerminologyModule { @@ -716,6 +717,16 @@ class LoincSubsetProcessor { dest: 'AccessoryFiles/ComponentHierarchyBySystem/ComponentHierarchyBySystem.csv', handler: 'processHierarchy' }, + { + source: 'AccessoryFiles/PartFile/PartRelatedCodeMapping.csv', + dest: 'AccessoryFiles/PartFile/PartRelatedCodeMapping.csv', + handler: 'processFirstColumnCodes' + }, + { + source: 'AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv', + dest: 'AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv', + handler: 'processFirstColumnCodes' + }, { source: 'AccessoryFiles/LinguisticVariants/LinguisticVariants.csv', dest: 'AccessoryFiles/LinguisticVariants/LinguisticVariants.csv', @@ -787,7 +798,8 @@ class LoincSubsetProcessor { path.join(destDir, 'AccessoryFiles/ConsumerName'), path.join(destDir, 'AccessoryFiles/AnswerFile'), path.join(destDir, 'AccessoryFiles/ComponentHierarchyBySystem'), - path.join(destDir, 'AccessoryFiles/LinguisticVariants') + path.join(destDir, 'AccessoryFiles/LinguisticVariants'), + path.join(destDir, 'AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable') ]; for (const dir of dirs) { @@ -879,6 +891,16 @@ class LoincSubsetProcessor { }); } + // keep the rows whose first column (the LOINC code or part) is in the subset + async processFirstColumnCodes(sourcePath, destPath) { + await this.processFileWithFilter(sourcePath, destPath, (line, lineNum) => { + if (lineNum === 1) return true; // Keep header + + const items = this.csvSplit(line, 1); + return this.targetCodes.has(this.removeQuotes(items[0])); + }); + } + async processAnswerLists(sourcePath, destPath) { await this.processFileWithFilter(sourcePath, destPath, (line, lineNum) => { if (lineNum === 1) return true; // Keep header @@ -1002,13 +1024,10 @@ class LoincSubsetProcessor { return; } - const readStream = fs.createReadStream(sourcePath); const writeStream = fs.createWriteStream(destPath); - const rl = readline.createInterface({ - input: readStream, - crlfDelay: Infinity - }); + // records, not lines: a quoted field can hold a line break + const rl = csvRecords(sourcePath); let lineNum = 0; let includedLines = 0; diff --git a/tx/openapi-operations.js b/tx/openapi-operations.js index 6fd2bdab..c5f2ca58 100644 --- a/tx/openapi-operations.js +++ b/tx/openapi-operations.js @@ -332,7 +332,8 @@ const OPERATIONS = [ p('name', 'string', '1', 'The closure table', { min: 1 }), p('concept', 'Coding', '*', 'A concept to add'), p('version', 'string', '1', 'Return the changes since this version of the table, instead of adding concepts'), - p('reset', 'boolean', '1', 'Start the table again') + p('reset', 'boolean', '1', 'Start the table again'), + p('dispose', 'boolean', '1', 'The client has finished with the table, and the server can delete it (not in the specification)') ], returns: 'ConceptMap', returnsDoc: 'The new relationships' diff --git a/tx/provider.js b/tx/provider.js index 66de3e93..43a65ddb 100644 --- a/tx/provider.js +++ b/tx/provider.js @@ -286,10 +286,13 @@ class Provider { } } + // Ask each code system version once: an implicit map can belong to one version + // only (a SNOMED CT edition's maps are named by its edition URI) let uris = new Set(); for (let csp of this.codeSystemFactories.values()) { - if (!uris.has(csp.system())) { - uris.add(csp.system()); + const key = csp.system() + '|' + csp.version(); + if (!uris.has(key)) { + uris.add(key); let cm = await csp.findImplicitConceptMap(url, version); if (cm) { return cm; @@ -343,16 +346,19 @@ class Provider { // version (every SNOMED edition, say) is asked, since whether a map exists and what // it says can depend on which version the source coding is in. The factory decides // from sourceVersion whether it is the one being asked about. + // The same goes for the target system: a map from another system into one SNOMED + // edition (LOINC codes to the concepts that carry them, say) belongs to that edition. + const targetBase = targetSystem.split('|')[0]; let uris = new Set(); let asked = new Set(); // a factory is registered under several keys (system, system|version...) for (let csp of this.codeSystemFactories.values()) { if (asked.has(csp)) { continue; } - if (csp.system() === sourceSystem || !uris.has(csp.system())) { + if (csp.system() === sourceSystem || csp.system() === targetBase || !uris.has(csp.system())) { uris.add(csp.system()); asked.add(csp); - await csp.findImplicitConceptMaps(conceptMaps, sourceSystem, sourceVersion, targetSystem); + await csp.findImplicitConceptMaps(conceptMaps, sourceSystem, sourceVersion, targetSystem, sourceCode); } } } diff --git a/tx/sct/structures.js b/tx/sct/structures.js index 8fdcd30a..679bf811 100644 --- a/tx/sct/structures.js +++ b/tx/sct/structures.js @@ -1358,7 +1358,7 @@ class SnomedFileReader { } // Determine settings based on cache version string (matching Pascal logic) - if (result.cacheVersion === '17') { // SNOMED_CACHE_VERSION_CURRENT + if (result.cacheVersion === '17' || result.cacheVersion === '18') { // SNOMED_CACHE_VERSION_CURRENT (18 = 17 + alternate identifiers) result.hasLangs = true; } else if (result.cacheVersion === '16') { // Assuming this is UTF8 version for now result.isUTF16 = false; @@ -1379,9 +1379,15 @@ class SnomedFileReader { result.activeRoots.push(this.readUInt64()); } - // Read default language LAST + // Read default language result.defaultLanguage = this.readInteger(); + // Alternate identifiers (version 18): [[scheme, module, code, conceptId], ...] + result.identifiers = []; + if (result.cacheVersion === '18') { + result.identifiers = JSON.parse(this.readBytes().toString('utf8')); + } + return result; } } diff --git a/tx/tx.fhir.org.yml b/tx/tx.fhir.org.yml index 9a2b3266..b254ffbd 100644 --- a/tx/tx.fhir.org.yml +++ b/tx/tx.fhir.org.yml @@ -20,7 +20,7 @@ sources: - internal:hgvs - ucum:tx/data/ucum-essence.xml - loinc:loinc-2.77-a.db - - loinc!:Loinc_2.82.db + - loinc!:Loinc_2.83.db - rxnorm:rxnorm_03022026.db - ndc:ndc-20211101.db - unii:unii_20240622.db @@ -32,7 +32,7 @@ sources: - snomed:sct_us_20230301.cache - snomed:sct_us_20240301.cache - snomed:sct_us_20250901.cache - - snomed:sct_test_20250909c.cache + - snomed:sct_test_20250909d.cache - cpt:CodeSystem-cpt.db|cpt-2023-fragment-0.1.db - omop:omop_v20250227.db - npm:hl7.terminology=tho diff --git a/tx/workers/closure.js b/tx/workers/closure.js index 241cd11d..b2ef1fb7 100644 --- a/tx/workers/closure.js +++ b/tx/workers/closure.js @@ -17,6 +17,12 @@ // already exists, unless reset = true, which wipes it // name + concept(s) add concepts; returns the entries they produce // name + version resync; returns every entry added after version +// name + dispose = true the client has finished with the table, so it is +// deleted. Not in the specification: the client is +// just telling the server it can let the table go. +// Disposing of a table that doesn't exist is fine +// (it may already have been pruned). It can't be +// combined with anything else // // The configuration is fixed when the table is initialised, and replayed on every later // call: tx-resource (the code systems it is built over, which the client does not send @@ -147,6 +153,7 @@ class ClosureWorker extends TerminologyWorker { const config = []; let versionParam = null; let reset = false; + let dispose = false; const ignored = []; for (const p of list) { if (p.name === 'concept') { @@ -157,6 +164,8 @@ class ClosureWorker extends TerminologyWorker { versionParam = p; } else if (p.name === 'reset') { reset = p.valueBoolean === true || p.valueString === 'true'; + } else if (p.name === 'dispose') { + dispose = p.valueBoolean === true || p.valueString === 'true'; } else if (p.name !== 'name') { // Ignored, as everywhere else in the server: a request carries parameters that // are not for the operation in hand. The tx-ecosystem runner, for one, adds a @@ -167,6 +176,17 @@ class ClosureWorker extends TerminologyWorker { if (ignored.length > 0) { this.log.debug(`$closure: ignored parameter(s) ${ignored.map(safeText).join(', ')}`); } + if (dispose) { + if (concepts.length > 0 || versionParam || reset || config.length > 0) { + throw this.closureIssue('invalid', 'CLOSURE_DISPOSE_NOT_ALONE', [], 'invalid-data', 400); + } + return await this.lockTable(name, async () => { + if (this.store.dropTable(name)) { + this.log.info(`closure table '${name}' disposed`); + } + return await this.buildConceptMap(name, 0, [], fhirVersion); + }); + } if (concepts.length > 0 && versionParam) { throw this.closureIssue('invalid', 'CLOSURE_CONCEPT_AND_VERSION', [], 'invalid-data', 400); } diff --git a/utilities/make-sct-loinc-subset.js b/utilities/make-sct-loinc-subset.js new file mode 100644 index 00000000..f026c321 --- /dev/null +++ b/utilities/make-sct-loinc-subset.js @@ -0,0 +1,250 @@ +#!/usr/bin/env node +/** + * Generates the LOINC Extension (LOINC ontology, module 11010000107, + * namespace 1010000) content for the SNOMED CT test edition that the + * tx-ecosystem tests use. + * + * The agreed scope for LOINC Extension support is: + * - $lookup on a SNOMED concept returns its LOINC code + * - $translate using the implicit map ?fhir_cm=11010000107 (the LOINC module) + * - $expand with a filter for concepts that have a LOINC code + * + * None of that needs the LOINC ontology's concept model, so this script takes + * a handful of real LOINC Extension concepts from the published release and + * copies their concept, description, language refset and identifier rows, + * but NOT their defining relationships or OWL axioms (those reference + * hundreds of concepts that are not in the test subset). Instead each concept + * gets a single generated IS A to an observable entity that is in the + * subset, and is made primitive (a fully defined concept defined only by an + * IS A would be equivalent to its parent). + * + * The two metadata concepts the identifiers hang off - the module concept and + * the LOINC identifier scheme - are copied too, re-parented to metadata + * concepts that are in the subset (their real parents are not). + * + * All rows are given the edition's effective time (20250909). Everything else + * - ids, terms, case significance, acceptability, LOINC codes - is as + * published. Rows are deterministic, so the script can be re-run safely. + * + * Usage: + * node utilities/make-sct-loinc-subset.js [tx/data/snomed] + * e.g. + * node utilities/make-sct-loinc-subset.js \ + * ~/archive/terminologies/SnomedCT_LOINCExtension_PRODUCTION_LO1010000_20261021T120000Z \ + * ../../igs/tx-ecosystem-ig/tx-source/snomed + */ + +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); + +const source = process.argv[2]; +if (!source) { + console.error('Usage: node utilities/make-sct-loinc-subset.js [target snomed folder]'); + process.exit(1); +} +const root = process.argv[3] || path.join(__dirname, '..', 'tx', 'data', 'snomed'); +const TAG = 'LO1010000'; +const ET = '20250909'; +const MODULE = '11010000107'; // LOINC Extension module +const EDITION_MODULE = '31000003106'; // the test edition module +const NAMESPACE = '1010000'; +const EOL = '\r\n'; + +// --- RF2 constants ------------------------------------------------------ + +const PRIMITIVE = '900000000000074008'; +const IS_A = '116680003'; +const INFERRED = '900000000000011006'; +const EXISTENTIAL = '900000000000451002'; +const OWL_AXIOM_REFSET = '733073007'; +const MODULE_DEPENDENCY_REFSET = '900000000000534007'; +const INT_CORE = '900000000000207008'; +const INT_MODEL = '900000000000012004'; +const INT_ET = '20250801'; // the International release the subset was extracted from + +// --- The subset --------------------------------------------------------- + +// LOINC code -> parent in the test subset (null = inactive, no relationships). +// The LOINC concept id is taken from the release's identifier file. +const loincCodes = [ + // all but the last are in the tx-ecosystem LOINC test subset, so they can be translated both ways + ['2823-3', '11953005'], // Potassium [Moles/volume] in Serum or Plasma -> Renal function + ['22760-3', '11953005'], // Potassium [Mass/volume] in Serum or Plasma -> Renal function + ['6298-4', '11953005'], // Potassium [Moles/volume] in Blood -> Renal function + ['2828-2', '11953005'], // Potassium [Moles/volume] in Urine -> Renal function + ['14402-2', '11953005'], // Creatinine [Mass/volume] in Gastric fluid -> Renal function + ['14394-1', '11953005'], // Urea nitrogen [Mass/volume] in Pleural fluid -> Renal function + ['104597-0', '363787002'], // Glucose [Mass/volume] in Venous blood by Glucometer -> Observable entity + ['95084-0', '363787002'], // C peptide [Mass/volume] in Serum or Plasma --2 hours post meal -> Observable entity + ['95095-6', '363787002'], // Glucagon [Mass/volume] in Serum or Plasma --2 hours post meal -> Observable entity + ['31157-1', '363787002'], // Carboxyhemoglobin [Mass/volume] in Blood -> Observable entity + ['95594-8', '363787002'], // Calcifediol [Mass/volume] in Capillary blood -> Observable entity + ['2067-7', null] // inactive in the release, but still has an (active) LOINC identifier; not in the LOINC test subset +]; + +// metadata concepts -> substitute parent in the test subset +const metadata = [ + [MODULE, '900000000000443000'], // LOINC Extension module -> Module (real parent 1201891009 not in subset) + ['30051010000102', '900000000000454005'] // LOINC code identifier -> Foundation metadata concept (real parent 900000000000453004 not in subset) +]; + +// --- Read the release --------------------------------------------------- + +function findFile(dir, prefix) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, entry.name); + if (entry.isDirectory()) { + const r = findFile(p, prefix); + if (r) return r; + } else if (entry.name.startsWith(prefix)) { + return p; + } + } + return null; +} + +function readRf2(prefix) { + const file = findFile(path.join(source, 'Snapshot'), prefix); + if (!file) throw new Error(`No ${prefix}* file in ${source}/Snapshot`); + const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/).filter(l => l.length > 0); + return { header: lines[0], rows: lines.slice(1).map(l => l.split('\t')) }; +} + +const identifiers = readRf2('sct2_Identifier_Snapshot'); +const conceptFile = readRf2('sct2_Concept_Snapshot'); +const descriptionFile = readRf2('sct2_Description_Snapshot'); +const languageFile = readRf2('der2_cRefset_LanguageSnapshot'); +const relationshipFile = readRf2('sct2_Relationship_Snapshot'); + +const conceptById = new Map(conceptFile.rows.map(r => [r[0], r])); +const identifierByCode = new Map(identifiers.rows.map(r => [r[0], r])); + +// --- Ids for generated rows --------------------------------------------- + +const d = [ + [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], + [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], + [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], + [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]]; +const p = [ + [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], + [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], + [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], [7, 0, 4, 6, 8, 1, 5, 3, 9, 2]]; +const inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]; + +function verhoeff(numStr) { + let c = 0; + const digits = numStr.split('').reverse().map(Number); + for (let i = 0; i < digits.length; i++) { + c = d[c][p[(i + 1) % 8][digits[i]]]; + } + return inv[c]; +} + +// Relationship ids are allocated from a high item range and checked against +// the release so they can never collide with a published relationship id. +const usedRelationshipIds = new Set(relationshipFile.rows.map(r => r[0])); +let nextItem = 99000001; +function relationshipId() { + for (;;) { + const body = `${nextItem++}${NAMESPACE}12`; + const id = body + verhoeff(body); + if (!usedRelationshipIds.has(id)) return id; + } +} + +function memberId(...parts) { + const h = crypto.createHash('sha1').update(['sct-loinc-subset', ...parts].join('|')).digest('hex'); + return `${h.slice(0, 8)}-${h.slice(8, 12)}-5${h.slice(13, 16)}-a${h.slice(17, 20)}-${h.slice(20, 32)}`; +} + +// --- Build rows --------------------------------------------------------- + +const wanted = []; // [conceptId, parent|null, loincCode|null] +for (const [code, parent] of loincCodes) { + const ident = identifierByCode.get(code); + if (!ident) throw new Error(`LOINC code ${code} has no identifier in the release`); + if (ident[3] !== MODULE) throw new Error(`Identifier for ${code} is in module ${ident[3]}, not ${MODULE}`); + wanted.push([ident[5], parent, code]); +} +for (const [id, parent] of metadata) wanted.push([id, parent, null]); + +const conceptRows = []; +const relationshipRows = []; +const owlRows = []; +const identifierRows = []; +const summary = []; +const wantedIds = new Set(wanted.map(w => w[0])); + +for (const [id, parent, code] of wanted) { + const c = conceptById.get(id); + if (!c) throw new Error(`Concept ${id} not found in the release`); + if (c[3] !== MODULE) throw new Error(`Concept ${id} is in module ${c[3]}, not ${MODULE}`); + const active = c[2]; + if (active === '1' && !parent) throw new Error(`Active concept ${id} needs a parent`); + if (active === '0' && parent) throw new Error(`Inactive concept ${id} must not be given a parent`); + conceptRows.push([id, ET, active, MODULE, PRIMITIVE]); + if (parent) { + relationshipRows.push([relationshipId(), ET, '1', MODULE, id, parent, '0', IS_A, INFERRED, EXISTENTIAL]); + owlRows.push([memberId('owl', id), ET, '1', MODULE, OWL_AXIOM_REFSET, id, `SubClassOf(:${id} :${parent})`]); + } + if (code) { + const ident = identifierByCode.get(code); + identifierRows.push([ident[0], ET, ident[2], ident[3], ident[4], ident[5]]); + } + summary.push({ id, code, active, parent }); +} + +const descriptionRows = descriptionFile.rows + .filter(r => wantedIds.has(r[4])) + .map(r => [r[0], ET, ...r.slice(2)]); +const descriptionIds = new Set(descriptionRows.map(r => r[0])); +for (const s of summary) { + const fsn = descriptionRows.find(r => r[4] === s.id && r[2] === '1' && r[6] === '900000000000003001'); + if (!fsn) throw new Error(`No active FSN for ${s.id}`); + s.fsn = fsn[7]; +} + +const languageRows = languageFile.rows + .filter(r => descriptionIds.has(r[5])) + .map(r => [r[0], ET, ...r.slice(2)]); + +const moduleDependencyRows = [ + [memberId('dep', MODULE, INT_CORE), ET, '1', MODULE, MODULE_DEPENDENCY_REFSET, INT_CORE, ET, INT_ET], + [memberId('dep', MODULE, INT_MODEL), ET, '1', MODULE, MODULE_DEPENDENCY_REFSET, INT_MODEL, ET, INT_ET], + [memberId('dep', EDITION_MODULE, MODULE), ET, '1', EDITION_MODULE, MODULE_DEPENDENCY_REFSET, MODULE, ET, ET] +]; + +// --- Write files -------------------------------------------------------- + +function sortRows(rows) { + return rows.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)); +} + +function write(rel, header, rows) { + const file = path.join(root, rel); + fs.mkdirSync(path.dirname(file), { recursive: true }); + fs.writeFileSync(file, [header, ...sortRows(rows).map(r => r.join('\t'))].join(EOL) + EOL, 'utf8'); + console.log(`${rows.length.toString().padStart(3)} rows ${rel}`); +} + +write(`Terminology/sct2_Concept_Snapshot_${TAG}_${ET}.txt`, + 'id\teffectiveTime\tactive\tmoduleId\tdefinitionStatusId', conceptRows); +write(`Terminology/sct2_Description_Snapshot-en_${TAG}_${ET}.txt`, + 'id\teffectiveTime\tactive\tmoduleId\tconceptId\tlanguageCode\ttypeId\tterm\tcaseSignificanceId', descriptionRows); +write(`Terminology/sct2_Relationship_Snapshot_${TAG}_${ET}.txt`, + 'id\teffectiveTime\tactive\tmoduleId\tsourceId\tdestinationId\trelationshipGroup\ttypeId\tcharacteristicTypeId\tmodifierId', relationshipRows); +write(`Terminology/sct2_sRefset_OWLExpressionSnapshot_${TAG}_${ET}.txt`, + 'id\teffectiveTime\tactive\tmoduleId\trefsetId\treferencedComponentId\towlExpression', owlRows); +write(`Terminology/sct2_Identifier_Snapshot_${TAG}_${ET}.txt`, + 'alternateIdentifier\teffectiveTime\tactive\tmoduleId\tidentifierSchemeId\treferencedComponentId', identifierRows); +write(`Refset/Language/der2_cRefset_LanguageSnapshot-en_${TAG}_${ET}.txt`, + 'id\teffectiveTime\tactive\tmoduleId\trefsetId\treferencedComponentId\tacceptabilityId', languageRows); +write(`Refset/Metadata/der2_ssRefset_ModuleDependencySnapshot_${TAG}_${ET}.txt`, + 'id\teffectiveTime\tactive\tmoduleId\trefsetId\treferencedComponentId\tsourceEffectiveTime\ttargetEffectiveTime', moduleDependencyRows); + +console.log('\nConcepts:'); +for (const s of summary) { + console.log(` ${s.id.padEnd(16)} ${(s.code || '').padEnd(8)} ${s.active === '0' ? '(inactive) ' : ''}${s.fsn}`); +}