fhirsmith 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/FHIRsmith.png +0 -0
- package/README.md +277 -0
- package/config-template.json +144 -0
- package/library/folder-setup.js +58 -0
- package/library/html-server.js +166 -0
- package/library/html.js +835 -0
- package/library/i18nsupport.js +259 -0
- package/library/languages.js +779 -0
- package/library/logger-telnet.js +205 -0
- package/library/logger.js +279 -0
- package/library/package-manager.js +876 -0
- package/library/utilities.js +196 -0
- package/library/version-utilities.js +1056 -0
- package/npmprojector/config-example.json +13 -0
- package/npmprojector/indexer.js +394 -0
- package/npmprojector/npmprojector.js +395 -0
- package/npmprojector/readme.md +174 -0
- package/npmprojector/watcher.js +335 -0
- package/package.json +119 -0
- package/packages/package-crawler.js +846 -0
- package/packages/packages-template.html +126 -0
- package/packages/packages.js +2838 -0
- package/passwords.ini +2 -0
- package/publisher/publisher-template.html +208 -0
- package/publisher/publisher.js +2167 -0
- package/publisher/task-draft.js +458 -0
- package/registry/api.js +735 -0
- package/registry/crawler.js +637 -0
- package/registry/model.js +513 -0
- package/registry/readme.md +243 -0
- package/registry/registry-data.json +121015 -0
- package/registry/registry-template.html +126 -0
- package/registry/registry.js +1395 -0
- package/registry/test-runner.js +237 -0
- package/root-template.html +124 -0
- package/server.js +524 -0
- package/shl/private-key.pem +5 -0
- package/shl/public-key.pem +18 -0
- package/shl/shl.js +1125 -0
- package/shl/vhl.js +69 -0
- package/static/FHIRsmith128.png +0 -0
- package/static/FHIRsmith16.png +0 -0
- package/static/FHIRsmith32.png +0 -0
- package/static/FHIRsmith64.png +0 -0
- package/static/assets/css/bootstrap-fhir.css +5302 -0
- package/static/assets/css/bootstrap-glyphicons.css +2 -0
- package/static/assets/css/bootstrap.css +4097 -0
- package/static/assets/css/jquery-ui.css +523 -0
- package/static/assets/css/jquery-ui.structure.css +863 -0
- package/static/assets/css/jquery-ui.structure.min.css +5 -0
- package/static/assets/css/jquery-ui.theme.css +439 -0
- package/static/assets/css/jquery-ui.theme.min.css +5 -0
- package/static/assets/css/jquery.ui.all.css +7 -0
- package/static/assets/css/modules.css +18 -0
- package/static/assets/css/project.css +367 -0
- package/static/assets/css/pygments-manni.css +66 -0
- package/static/assets/css/tags.css +74 -0
- package/static/assets/css/xml.css +2 -0
- package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
- package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
- package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
- package/static/assets/ico/favicon.ico +0 -0
- package/static/assets/ico/favicon.png +0 -0
- package/static/assets/images/fhir-logo-www.png +0 -0
- package/static/assets/images/fhir-logo.png +0 -0
- package/static/assets/images/hl7-logo.png +0 -0
- package/static/assets/images/logo_ansinew.jpg +0 -0
- package/static/assets/images/search.png +0 -0
- package/static/assets/images/stripe.png +0 -0
- package/static/assets/images/target.png +0 -0
- package/static/assets/images/tx-registry-root.gif +0 -0
- package/static/assets/images/tx-registry.png +0 -0
- package/static/assets/images/tx-server.png +0 -0
- package/static/assets/images/tx-version.png +0 -0
- package/static/assets/js/bootstrap.min.js +6 -0
- package/static/assets/js/fhir-gw.js +259 -0
- package/static/assets/js/fhir.js +2 -0
- package/static/assets/js/html5shiv.js +8 -0
- package/static/assets/js/jcookie.js +96 -0
- package/static/assets/js/jquery-ui.min.js +6 -0
- package/static/assets/js/jquery.js +10716 -0
- package/static/assets/js/jquery.min.js +2 -0
- package/static/assets/js/jquery.ui.core.js +314 -0
- package/static/assets/js/jquery.ui.draggable.js +825 -0
- package/static/assets/js/jquery.ui.mouse.js +162 -0
- package/static/assets/js/jquery.ui.resizable.js +842 -0
- package/static/assets/js/jquery.ui.widget.js +268 -0
- package/static/assets/js/json2.js +487 -0
- package/static/assets/js/jtip.js +97 -0
- package/static/assets/js/respond.min.js +6 -0
- package/static/assets/js/statuspage.js +70 -0
- package/static/assets/js/xml.js +2 -0
- package/static/dist/js/bootstrap.js +1964 -0
- package/static/favicon.png +0 -0
- package/static/fhir.css +626 -0
- package/static/icon-fhir-16.png +0 -0
- package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
- package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- package/static/images/ui-icons_222222_256x240.png +0 -0
- package/static/images/ui-icons_228ef1_256x240.png +0 -0
- package/static/images/ui-icons_ef8c08_256x240.png +0 -0
- package/static/images/ui-icons_ffd27a_256x240.png +0 -0
- package/static/images/ui-icons_ffffff_256x240.png +0 -0
- package/static/js/jquery.effects.blind.js +49 -0
- package/static/js/jquery.effects.bounce.js +78 -0
- package/static/js/jquery.effects.clip.js +54 -0
- package/static/js/jquery.effects.core.js +763 -0
- package/static/js/jquery.effects.drop.js +50 -0
- package/static/js/jquery.effects.explode.js +79 -0
- package/static/js/jquery.effects.fade.js +32 -0
- package/static/js/jquery.effects.fold.js +56 -0
- package/static/js/jquery.effects.highlight.js +50 -0
- package/static/js/jquery.effects.pulsate.js +51 -0
- package/static/js/jquery.effects.scale.js +178 -0
- package/static/js/jquery.effects.shake.js +57 -0
- package/static/js/jquery.effects.slide.js +50 -0
- package/static/js/jquery.effects.transfer.js +45 -0
- package/static/js/jquery.ui.accordion.js +611 -0
- package/static/js/jquery.ui.autocomplete.js +612 -0
- package/static/js/jquery.ui.button.js +416 -0
- package/static/js/jquery.ui.datepicker.js +1823 -0
- package/static/js/jquery.ui.dialog.js +878 -0
- package/static/js/jquery.ui.droppable.js +296 -0
- package/static/js/jquery.ui.position.js +252 -0
- package/static/js/jquery.ui.progressbar.js +109 -0
- package/static/js/jquery.ui.selectable.js +266 -0
- package/static/js/jquery.ui.slider.js +666 -0
- package/static/js/jquery.ui.sortable.js +1077 -0
- package/static/js/jquery.ui.tabs.js +758 -0
- package/stats.js +80 -0
- package/test-cache/vsac/vsac-valuesets.db +0 -0
- package/token/nginx_passport_setup.md +383 -0
- package/token/security_guide.md +294 -0
- package/token/token-template.html +330 -0
- package/token/token.js +1300 -0
- package/translations/Messages.properties +1510 -0
- package/translations/Messages_ar.properties +1399 -0
- package/translations/Messages_de.properties +836 -0
- package/translations/Messages_es.properties +737 -0
- package/translations/Messages_fr.properties +1 -0
- package/translations/Messages_ja.properties +893 -0
- package/translations/Messages_nl.properties +1357 -0
- package/translations/Messages_pt.properties +1302 -0
- package/translations/Messages_ru.properties +1 -0
- package/translations/Messages_uz.properties +1 -0
- package/translations/Messages_zh.properties +1 -0
- package/translations/rendering-phrases.properties +1128 -0
- package/translations/rendering-phrases_ar.properties +1091 -0
- package/translations/rendering-phrases_de.properties +6 -0
- package/translations/rendering-phrases_es.properties +6 -0
- package/translations/rendering-phrases_fr.properties +624 -0
- package/translations/rendering-phrases_ja.properties +21 -0
- package/translations/rendering-phrases_nl.properties +970 -0
- package/translations/rendering-phrases_pt.properties +1020 -0
- package/translations/rendering-phrases_ru.properties +1094 -0
- package/translations/rendering-phrases_uz.properties +1 -0
- package/translations/rendering-phrases_zh.properties +1 -0
- package/tx/README.md +418 -0
- package/tx/cm/cm-api.js +110 -0
- package/tx/cm/cm-database.js +735 -0
- package/tx/cm/cm-package.js +325 -0
- package/tx/cs/cs-api.js +789 -0
- package/tx/cs/cs-areacode.js +615 -0
- package/tx/cs/cs-country.js +1110 -0
- package/tx/cs/cs-cpt.js +785 -0
- package/tx/cs/cs-cs.js +1579 -0
- package/tx/cs/cs-currency.js +539 -0
- package/tx/cs/cs-db.js +1321 -0
- package/tx/cs/cs-hgvs.js +329 -0
- package/tx/cs/cs-lang.js +465 -0
- package/tx/cs/cs-loinc.js +1485 -0
- package/tx/cs/cs-mimetypes.js +238 -0
- package/tx/cs/cs-ndc.js +704 -0
- package/tx/cs/cs-omop.js +1025 -0
- package/tx/cs/cs-provider-api.js +43 -0
- package/tx/cs/cs-provider-list.js +37 -0
- package/tx/cs/cs-rxnorm.js +808 -0
- package/tx/cs/cs-snomed.js +1102 -0
- package/tx/cs/cs-ucum.js +514 -0
- package/tx/cs/cs-unii.js +271 -0
- package/tx/cs/cs-uri.js +218 -0
- package/tx/cs/cs-usstates.js +305 -0
- package/tx/dev.fhir.org.yml +14 -0
- package/tx/fixtures/test-cases-setup.json +18 -0
- package/tx/fixtures/test-cases.yml +16 -0
- package/tx/html/codesystem-operations.liquid +25 -0
- package/tx/html/home-metrics.liquid +247 -0
- package/tx/html/operations-form.liquid +148 -0
- package/tx/html/search-form.liquid +62 -0
- package/tx/html/tx-template.html +133 -0
- package/tx/html/valueset-operations.liquid +54 -0
- package/tx/importers/atc-to-fhir.js +316 -0
- package/tx/importers/import-loinc.module.js +1536 -0
- package/tx/importers/import-ndc.module.js +1088 -0
- package/tx/importers/import-rxnorm.module.js +898 -0
- package/tx/importers/import-sct.module.js +2457 -0
- package/tx/importers/import-unii.module.js +601 -0
- package/tx/importers/readme.md +453 -0
- package/tx/importers/subset-loinc.module.js +1081 -0
- package/tx/importers/subset-rxnorm.module.js +938 -0
- package/tx/importers/tx-import-base.js +351 -0
- package/tx/importers/tx-import-settings.js +310 -0
- package/tx/importers/tx-import.js +357 -0
- package/tx/library/canonical-resource.js +88 -0
- package/tx/library/capabilitystatement.js +292 -0
- package/tx/library/codesystem.js +774 -0
- package/tx/library/conceptmap.js +568 -0
- package/tx/library/designations.js +932 -0
- package/tx/library/errors.js +77 -0
- package/tx/library/extensions.js +117 -0
- package/tx/library/namingsystem.js +322 -0
- package/tx/library/operation-outcome.js +127 -0
- package/tx/library/parameters.js +105 -0
- package/tx/library/renderer.js +1559 -0
- package/tx/library/terminologycapabilities.js +418 -0
- package/tx/library/ucum-parsers.js +1029 -0
- package/tx/library/ucum-service.js +370 -0
- package/tx/library/ucum-types.js +1099 -0
- package/tx/library/valueset.js +543 -0
- package/tx/library.js +676 -0
- package/tx/ocl/cm-ocl.js +106 -0
- package/tx/ocl/cs-ocl.js +39 -0
- package/tx/ocl/vs-ocl.js +105 -0
- package/tx/operation-context.js +568 -0
- package/tx/params.js +613 -0
- package/tx/provider.js +403 -0
- package/tx/sct/ecl.js +1560 -0
- package/tx/sct/expressions.js +2077 -0
- package/tx/sct/structures.js +1396 -0
- package/tx/tx-html.js +1063 -0
- package/tx/tx.fhir.org.yml +39 -0
- package/tx/tx.js +927 -0
- package/tx/vs/vs-api.js +112 -0
- package/tx/vs/vs-database.js +786 -0
- package/tx/vs/vs-package.js +358 -0
- package/tx/vs/vs-vsac.js +366 -0
- package/tx/workers/batch-validate.js +129 -0
- package/tx/workers/batch.js +361 -0
- package/tx/workers/closure.js +32 -0
- package/tx/workers/expand.js +1845 -0
- package/tx/workers/lookup.js +407 -0
- package/tx/workers/metadata.js +467 -0
- package/tx/workers/operations.js +34 -0
- package/tx/workers/read.js +164 -0
- package/tx/workers/search.js +384 -0
- package/tx/workers/subsumes.js +334 -0
- package/tx/workers/translate.js +492 -0
- package/tx/workers/validate.js +2504 -0
- package/tx/workers/worker.js +904 -0
- package/tx/xml/capabilitystatement-xml.js +63 -0
- package/tx/xml/codesystem-xml.js +62 -0
- package/tx/xml/conceptmap-xml.js +65 -0
- package/tx/xml/namingsystem-xml.js +65 -0
- package/tx/xml/operationoutcome-xml.js +127 -0
- package/tx/xml/parameters-xml.js +312 -0
- package/tx/xml/terminologycapabilities-xml.js +64 -0
- package/tx/xml/valueset-xml.js +64 -0
- package/tx/xml/xml-base.js +603 -0
- package/vcl/vcl-parser.js +1098 -0
- package/vcl/vcl.js +253 -0
- package/windows-install.js +19 -0
- package/xig/xig-template.html +124 -0
- package/xig/xig.js +3049 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { XMLParser } = require('fast-xml-parser');
|
|
3
|
+
|
|
4
|
+
// Configuration
|
|
5
|
+
const ATC_FILE = process.argv[2] || '2025_ATC.xml';
|
|
6
|
+
const DDD_FILE = process.argv[3] || '2025_ATC_ddd.xml';
|
|
7
|
+
const OUTPUT_FILE = process.argv[4] || 'atc-codesystem.json';
|
|
8
|
+
|
|
9
|
+
const PROPERTY_GROUP_EXT_URL = 'http://hl7.org/fhir/property.group';
|
|
10
|
+
|
|
11
|
+
// Parse XML files
|
|
12
|
+
function parseXML(filePath) {
|
|
13
|
+
const xml = fs.readFileSync(filePath, 'utf-8');
|
|
14
|
+
const parser = new XMLParser({
|
|
15
|
+
ignoreAttributes: false,
|
|
16
|
+
attributeNamePrefix: '',
|
|
17
|
+
allowBooleanAttributes: true
|
|
18
|
+
});
|
|
19
|
+
return parser.parse(xml);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Extract rows from parsed XML
|
|
23
|
+
function extractRows(parsed) {
|
|
24
|
+
const data = parsed.xml['rs:data'];
|
|
25
|
+
let rows = data['z:row'];
|
|
26
|
+
if (!Array.isArray(rows)) {
|
|
27
|
+
rows = [rows];
|
|
28
|
+
}
|
|
29
|
+
return rows;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Get parent code for hierarchy
|
|
33
|
+
function getParentCode(code) {
|
|
34
|
+
const len = code.trim().length;
|
|
35
|
+
switch (len) {
|
|
36
|
+
case 1: return null; // No parent
|
|
37
|
+
case 3: return code.substring(0, 1); // A01 -> A
|
|
38
|
+
case 4: return code.substring(0, 3); // A01A -> A01
|
|
39
|
+
case 5: return code.substring(0, 4); // A01AA -> A01A
|
|
40
|
+
case 7: return code.substring(0, 5); // A01AA01 -> A01AA
|
|
41
|
+
default: return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Build DDD lookup map (ATCCode -> array of DDD entries)
|
|
46
|
+
function buildDDDMap(dddRows) {
|
|
47
|
+
const map = new Map();
|
|
48
|
+
|
|
49
|
+
for (const row of dddRows) {
|
|
50
|
+
const code = row.ATCCode?.trim();
|
|
51
|
+
if (!code) continue;
|
|
52
|
+
|
|
53
|
+
const entry = {
|
|
54
|
+
ddd: row.DDD,
|
|
55
|
+
unit: row.UnitType,
|
|
56
|
+
admRoute: row.AdmCode,
|
|
57
|
+
comment: row.DDDComment
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (!map.has(code)) {
|
|
61
|
+
map.set(code, []);
|
|
62
|
+
}
|
|
63
|
+
map.get(code).push(entry);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return map;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Create property with optional group extension
|
|
70
|
+
function createProperty(code, value, groupId = null, valueType = 'string') {
|
|
71
|
+
const prop = {
|
|
72
|
+
code: code
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
if (valueType === 'code') {
|
|
76
|
+
prop.valueCode = value;
|
|
77
|
+
} else {
|
|
78
|
+
prop.valueString = value;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (groupId !== null) {
|
|
82
|
+
prop.extension = [{
|
|
83
|
+
url: PROPERTY_GROUP_EXT_URL,
|
|
84
|
+
valueCode: String(groupId)
|
|
85
|
+
}];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return prop;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Build concept from ATC row with DDD data
|
|
92
|
+
function buildConcept(atcRow, dddMap) {
|
|
93
|
+
const code = atcRow.ATCCode?.trim();
|
|
94
|
+
const name = atcRow.Name?.trim();
|
|
95
|
+
const comment = atcRow.Comment?.trim();
|
|
96
|
+
|
|
97
|
+
if (!code) return null;
|
|
98
|
+
|
|
99
|
+
const concept = {
|
|
100
|
+
code: code,
|
|
101
|
+
display: name
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const properties = [];
|
|
105
|
+
|
|
106
|
+
// Add comment property (non-grouped) if present
|
|
107
|
+
if (comment) {
|
|
108
|
+
properties.push(createProperty('comment', comment));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Add DDD properties with grouping
|
|
112
|
+
const dddEntries = dddMap.get(code) || [];
|
|
113
|
+
let groupId = 1;
|
|
114
|
+
|
|
115
|
+
for (const entry of dddEntries) {
|
|
116
|
+
// Only create group if there's actual DDD data
|
|
117
|
+
const hasDDD = entry.ddd !== undefined && entry.ddd !== null && entry.ddd;
|
|
118
|
+
const hasUnit = entry.unit !== undefined && entry.unit !== null && entry.unit;
|
|
119
|
+
const hasRoute = entry.admRoute !== undefined && entry.admRoute !== null && entry.admRoute;
|
|
120
|
+
const hasComment = entry.comment !== undefined && entry.comment !== null && entry.comment;
|
|
121
|
+
|
|
122
|
+
// Skip if no meaningful data
|
|
123
|
+
if (!hasDDD && !hasUnit && !hasRoute && !hasComment) continue;
|
|
124
|
+
|
|
125
|
+
if (hasDDD) {
|
|
126
|
+
properties.push(createProperty('dddValue', entry.ddd, groupId));
|
|
127
|
+
}
|
|
128
|
+
if (hasUnit) {
|
|
129
|
+
properties.push(createProperty('dddUnit', entry.unit, groupId));
|
|
130
|
+
}
|
|
131
|
+
if (hasRoute) {
|
|
132
|
+
properties.push(createProperty('dddAdmRoute', entry.admRoute, groupId, 'code'));
|
|
133
|
+
}
|
|
134
|
+
if (hasComment) {
|
|
135
|
+
properties.push(createProperty('dddComment', entry.comment, groupId));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
groupId++;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (properties.length > 0) {
|
|
142
|
+
concept.property = properties;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return concept;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Main conversion function
|
|
149
|
+
function convertATCtoFHIR(atcFile, dddFile) {
|
|
150
|
+
console.log(`Parsing ${atcFile}...`);
|
|
151
|
+
const atcParsed = parseXML(atcFile);
|
|
152
|
+
const atcRows = extractRows(atcParsed);
|
|
153
|
+
console.log(` Found ${atcRows.length} ATC codes`);
|
|
154
|
+
|
|
155
|
+
console.log(`Parsing ${dddFile}...`);
|
|
156
|
+
const dddParsed = parseXML(dddFile);
|
|
157
|
+
const dddRows = extractRows(dddParsed);
|
|
158
|
+
console.log(` Found ${dddRows.length} DDD entries`);
|
|
159
|
+
|
|
160
|
+
console.log('Building DDD lookup map...');
|
|
161
|
+
const dddMap = buildDDDMap(dddRows);
|
|
162
|
+
console.log(` ${dddMap.size} codes have DDD data`);
|
|
163
|
+
|
|
164
|
+
console.log('Building FHIR CodeSystem...');
|
|
165
|
+
|
|
166
|
+
const codeSystem = {
|
|
167
|
+
resourceType: 'CodeSystem',
|
|
168
|
+
id: 'atc',
|
|
169
|
+
url: 'http://www.whocc.no/atc',
|
|
170
|
+
identifier: [
|
|
171
|
+
{
|
|
172
|
+
use: 'official',
|
|
173
|
+
system: 'urn:ietf:rfc:3986',
|
|
174
|
+
value: 'urn:oid:2.16.840.1.113883.6.73'
|
|
175
|
+
}
|
|
176
|
+
],
|
|
177
|
+
version: '2025',
|
|
178
|
+
name: 'ATC_classification_system',
|
|
179
|
+
title: 'ATC classification system',
|
|
180
|
+
status: 'active',
|
|
181
|
+
experimental: false,
|
|
182
|
+
date: '2025-01-07',
|
|
183
|
+
publisher: 'WHO Collaborating Centre for Drug Statistics Methodology - Norwegian Institute of Public Health',
|
|
184
|
+
contact: [
|
|
185
|
+
{
|
|
186
|
+
name: 'WHO Collaborating Centre for Drug Statistics Methodology - Norwegian Institute of Public Health',
|
|
187
|
+
telecom: [
|
|
188
|
+
{
|
|
189
|
+
system: 'url',
|
|
190
|
+
value: 'http://www.whocc.no'
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
description: 'Anatomical Therapeutic Chemical (ATC) classification system',
|
|
196
|
+
copyright: 'WHO Collaborating Centre for Drug Statistics Methodology, Oslo, Norway. Use of all or parts of the material requires reference to the WHO Collaborating Centre for Drug Statistics Methodology. Copying and distribution for commercial purposes is not allowed. Changing or manipulating the material is not allowed.',
|
|
197
|
+
caseSensitive: true,
|
|
198
|
+
hierarchyMeaning: 'is-a',
|
|
199
|
+
content: 'complete',
|
|
200
|
+
count: 0, // Will be set after building concepts
|
|
201
|
+
property: [
|
|
202
|
+
{
|
|
203
|
+
code: 'comment',
|
|
204
|
+
description: 'General comment or note about the ATC code',
|
|
205
|
+
type: 'string'
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
code: 'dddValue',
|
|
209
|
+
description: 'Defined Daily Dose numeric value',
|
|
210
|
+
type: 'string'
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
code: 'dddUnit',
|
|
214
|
+
description: 'Unit of measurement for the DDD (mg, g, mcg, ml, etc.)',
|
|
215
|
+
type: 'string'
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
code: 'dddAdmRoute',
|
|
219
|
+
description: 'Administration route code (O=Oral, P=Parenteral, R=Rectal, N=Nasal, TD=Transdermal, V=Vaginal, etc.)',
|
|
220
|
+
type: 'code'
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
code: 'dddComment',
|
|
224
|
+
description: 'Qualifier or comment for the DDD entry (e.g., formulation type)',
|
|
225
|
+
type: 'string'
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
concept: []
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Build flat concepts first
|
|
232
|
+
const conceptMap = new Map();
|
|
233
|
+
for (const row of atcRows) {
|
|
234
|
+
const concept = buildConcept(row, dddMap);
|
|
235
|
+
if (concept) {
|
|
236
|
+
conceptMap.set(concept.code, concept);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Build nested hierarchy
|
|
241
|
+
const rootConcepts = [];
|
|
242
|
+
|
|
243
|
+
for (const [code, concept] of conceptMap) {
|
|
244
|
+
const parentCode = getParentCode(code);
|
|
245
|
+
|
|
246
|
+
if (parentCode === null) {
|
|
247
|
+
// Top-level concept
|
|
248
|
+
rootConcepts.push(concept);
|
|
249
|
+
} else {
|
|
250
|
+
// Find parent and add as child
|
|
251
|
+
const parent = conceptMap.get(parentCode);
|
|
252
|
+
if (parent) {
|
|
253
|
+
if (!parent.concept) {
|
|
254
|
+
parent.concept = [];
|
|
255
|
+
}
|
|
256
|
+
parent.concept.push(concept);
|
|
257
|
+
} else {
|
|
258
|
+
// Parent not found, add to root (shouldn't happen with valid data)
|
|
259
|
+
console.warn(`Warning: Parent ${parentCode} not found for ${code}`);
|
|
260
|
+
rootConcepts.push(concept);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
codeSystem.concept = rootConcepts;
|
|
266
|
+
codeSystem.count = conceptMap.size;
|
|
267
|
+
|
|
268
|
+
console.log(` Generated ${conceptMap.size} concepts in nested hierarchy`);
|
|
269
|
+
|
|
270
|
+
return codeSystem;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Run conversion
|
|
274
|
+
try {
|
|
275
|
+
const codeSystem = convertATCtoFHIR(ATC_FILE, DDD_FILE);
|
|
276
|
+
|
|
277
|
+
console.log(`Writing to ${OUTPUT_FILE}...`);
|
|
278
|
+
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(codeSystem, null, 2));
|
|
279
|
+
console.log('Done!');
|
|
280
|
+
|
|
281
|
+
// Print some stats
|
|
282
|
+
function countConcepts(concepts) {
|
|
283
|
+
let count = 0;
|
|
284
|
+
for (const c of concepts) {
|
|
285
|
+
count++;
|
|
286
|
+
if (c.concept) {
|
|
287
|
+
count += countConcepts(c.concept);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return count;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function countWithDDD(concepts) {
|
|
294
|
+
let count = 0;
|
|
295
|
+
for (const c of concepts) {
|
|
296
|
+
if (c.property?.some(p => p.code === 'dddValue')) {
|
|
297
|
+
count++;
|
|
298
|
+
}
|
|
299
|
+
if (c.concept) {
|
|
300
|
+
count += countWithDDD(c.concept);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return count;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const totalConcepts = countConcepts(codeSystem.concept);
|
|
307
|
+
const withDDD = countWithDDD(codeSystem.concept);
|
|
308
|
+
console.log(`\nStatistics:`);
|
|
309
|
+
console.log(` Total concepts: ${totalConcepts}`);
|
|
310
|
+
console.log(` Top-level concepts: ${codeSystem.concept.length}`);
|
|
311
|
+
console.log(` Concepts with DDD data: ${withDDD}`);
|
|
312
|
+
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error('Error:', error.message);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|