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,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UCUM Service - Main service class for UCUM operations
|
|
3
|
+
* BSD 3-Clause License
|
|
4
|
+
* Copyright (c) 2006+, Health Intersections Pty Ltd
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
UcumException, Pair, Registry, UcumVersionDetails
|
|
9
|
+
} = require('./ucum-types.js');
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
ExpressionParser, ExpressionComposer, FormalStructureComposer,
|
|
13
|
+
UcumEssenceParser, Search, Converter, UcumValidator
|
|
14
|
+
} = require('./ucum-parsers.js');
|
|
15
|
+
|
|
16
|
+
// UCUM Service - main service class for UCUM operations
|
|
17
|
+
class UcumService {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.model = null;
|
|
20
|
+
this.handlers = new Registry();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
init(xmlContent) {
|
|
24
|
+
const parser = new UcumEssenceParser();
|
|
25
|
+
this.model = parser.parse(xmlContent);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Given a unit, return a formal description of what the units stand for using
|
|
30
|
+
* full names
|
|
31
|
+
* @param {string} unit the unit code
|
|
32
|
+
* @return {string} formal description
|
|
33
|
+
* @throws {UcumException}
|
|
34
|
+
*/
|
|
35
|
+
analyse(unit) {
|
|
36
|
+
if (!unit || !unit.trim()) {
|
|
37
|
+
return "(unity)";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!this.model) {
|
|
41
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const term = new ExpressionParser(this.model).parse(unit);
|
|
45
|
+
return new FormalStructureComposer().compose(term);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Convert a unit to its canonical form
|
|
50
|
+
* @param {string} unit the unit code
|
|
51
|
+
* @return {string} canonical units
|
|
52
|
+
* @throws {UcumException}
|
|
53
|
+
*/
|
|
54
|
+
getCanonicalUnits(unit) {
|
|
55
|
+
if (!unit || !unit.trim()) {
|
|
56
|
+
throw new UcumException("getCanonicalUnits: unit must not be null or empty");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!this.model) {
|
|
60
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const term = new ExpressionParser(this.model).parse(unit);
|
|
65
|
+
const convertedTerm = new Converter(this.model, this.handlers).convert(term);
|
|
66
|
+
return new ExpressionComposer().compose(convertedTerm, false);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
e.message = `Error processing ${unit}: ${e.message}`;
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if two units are comparable (can be converted to same canonical form)
|
|
75
|
+
* @param {string} units1 first unit
|
|
76
|
+
* @param {string} units2 second unit
|
|
77
|
+
* @return {boolean} true if comparable
|
|
78
|
+
* @throws {UcumException}
|
|
79
|
+
*/
|
|
80
|
+
isComparable(units1, units2) {
|
|
81
|
+
if (!units1 || !units2) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const u1 = this.getCanonicalUnits(units1);
|
|
87
|
+
const u2 = this.getCanonicalUnits(units2);
|
|
88
|
+
return u1 === u2;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.error('Error message:', e.message);
|
|
91
|
+
console.error('Stack trace:', e.stack);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Divide one quantity by another
|
|
98
|
+
* @param {Pair} dividend the base
|
|
99
|
+
* @param {Pair} divisor the multiplier (not that order matters)
|
|
100
|
+
* @return {Pair} the result
|
|
101
|
+
* @throws {UcumException}
|
|
102
|
+
*/
|
|
103
|
+
multiply(o1, o2) {
|
|
104
|
+
const res = new Pair(o1.getValue().multiply(o2.getValue()), o1.getCode() +"."+o2.getCode());
|
|
105
|
+
return this.getCanonicalForm(res);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Divide one quantity by another
|
|
109
|
+
* @param {Pair} dividend the dividend
|
|
110
|
+
* @param {Pair} divisor the divisor
|
|
111
|
+
* @return {Pair} the result
|
|
112
|
+
* @throws {UcumException}
|
|
113
|
+
*/
|
|
114
|
+
divideBy(dividend, divisor) {
|
|
115
|
+
const dividendCode = dividend.getCode();
|
|
116
|
+
const divisorCode = divisor.getCode();
|
|
117
|
+
|
|
118
|
+
const resultValue = dividend.getValue().divide(divisor.getValue());
|
|
119
|
+
const resultCode = (dividendCode.includes("/") || dividendCode.includes("*") ?
|
|
120
|
+
"(" + dividendCode + ")" : dividendCode) + "/" +
|
|
121
|
+
(divisorCode.includes("/") || divisorCode.includes("*") ?
|
|
122
|
+
"(" + divisorCode + ")" : divisorCode);
|
|
123
|
+
|
|
124
|
+
const result = new Pair(resultValue, resultCode);
|
|
125
|
+
return this.getCanonicalForm(result);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Convert a Pair to its canonical form
|
|
130
|
+
* @param {Pair} pair the pair to convert
|
|
131
|
+
* @return {Pair} canonical form pair
|
|
132
|
+
* @throws {UcumException}
|
|
133
|
+
*/
|
|
134
|
+
getCanonicalForm(pair) {
|
|
135
|
+
if (!pair) {
|
|
136
|
+
throw new UcumException("getCanonicalForm: value must not be null");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const code = pair.getCode();
|
|
140
|
+
if (!code || !code.trim()) {
|
|
141
|
+
throw new UcumException("getCanonicalForm: value.code must not be null or empty");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!this.model) {
|
|
145
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const term = new ExpressionParser(this.model).parse(code);
|
|
149
|
+
const c = new Converter(this.model, this.handlers).convert(term);
|
|
150
|
+
|
|
151
|
+
let p = null;
|
|
152
|
+
if (pair.getValue() === null || pair.getValue() === undefined) {
|
|
153
|
+
p = new Pair(null, new ExpressionComposer().compose(c, false));
|
|
154
|
+
} else {
|
|
155
|
+
const newValue = pair.getValue().multiply(c.getValue());
|
|
156
|
+
p = new Pair(newValue, new ExpressionComposer().compose(c, false));
|
|
157
|
+
|
|
158
|
+
if (pair.getValue().isWholeNumber()) {
|
|
159
|
+
// whole numbers are tricky - they have implied infinite precision, but we need to check for digit errors in the last couple of digits
|
|
160
|
+
if (p.getValue().checkForCouldBeWholeNumber) {
|
|
161
|
+
p.getValue().checkForCouldBeWholeNumber();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return p;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Validate the UCUM model
|
|
171
|
+
* @return {Array<string>} list of validation errors
|
|
172
|
+
*/
|
|
173
|
+
validateUCUM() {
|
|
174
|
+
if (!this.model) {
|
|
175
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const validator = new UcumValidator(this.model, this.handlers);
|
|
179
|
+
return validator.validate();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get UCUM version identification
|
|
184
|
+
* @return {UcumVersionDetails} version details
|
|
185
|
+
*/
|
|
186
|
+
ucumIdentification() {
|
|
187
|
+
if (!this.model) {
|
|
188
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return new UcumVersionDetails(this.model.revisionDate, this.model.version);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Validate a unit expression
|
|
196
|
+
* @param {string} unit the unit to validate
|
|
197
|
+
* @return {string} null if valid, error message if invalid
|
|
198
|
+
*/
|
|
199
|
+
validate(unit) {
|
|
200
|
+
if (!unit) {
|
|
201
|
+
return "unit must not be null";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!this.model) {
|
|
205
|
+
return "UCUM service not initialized - call init() first";
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
new ExpressionParser(this.model).parse(unit);
|
|
210
|
+
return null;
|
|
211
|
+
} catch (e) {
|
|
212
|
+
return e.message;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Check if a unit expression is valid
|
|
218
|
+
* @param {string} unit the unit to check
|
|
219
|
+
* @return {boolean} true if valid, false if invalid
|
|
220
|
+
*/
|
|
221
|
+
isValidUnit(unit) {
|
|
222
|
+
return this.validate(unit) === null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Convert a value from one unit to another
|
|
227
|
+
* @param {Decimal} value the value to convert
|
|
228
|
+
* @param {string} sourceUnit source unit
|
|
229
|
+
* @param {string} destUnit destination unit
|
|
230
|
+
* @return {Decimal} converted value
|
|
231
|
+
* @throws {UcumException}
|
|
232
|
+
*/
|
|
233
|
+
convert(value, sourceUnit, destUnit) {
|
|
234
|
+
if (!value) {
|
|
235
|
+
throw new UcumException("convert: value must not be null");
|
|
236
|
+
}
|
|
237
|
+
if (!sourceUnit || !sourceUnit.trim()) {
|
|
238
|
+
throw new UcumException("convert: sourceUnit must not be null or empty");
|
|
239
|
+
}
|
|
240
|
+
if (!destUnit || !destUnit.trim()) {
|
|
241
|
+
throw new UcumException("convert: destUnit must not be null or empty");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!this.model) {
|
|
245
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (sourceUnit === destUnit) {
|
|
249
|
+
return value;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const src = new Converter(this.model, this.handlers).convert(
|
|
253
|
+
new ExpressionParser(this.model).parse(sourceUnit)
|
|
254
|
+
);
|
|
255
|
+
const dst = new Converter(this.model, this.handlers).convert(
|
|
256
|
+
new ExpressionParser(this.model).parse(destUnit)
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const s = new ExpressionComposer().compose(src, false);
|
|
260
|
+
const d = new ExpressionComposer().compose(dst, false);
|
|
261
|
+
|
|
262
|
+
if (s !== d) {
|
|
263
|
+
throw new UcumException(
|
|
264
|
+
`Unable to convert between units ${sourceUnit} and ${destUnit} as they do not have matching canonical forms (${s} and ${d} respectively)`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const canValue = value.multiply(src.getValue());
|
|
269
|
+
let res = canValue.divide(dst.getValue());
|
|
270
|
+
|
|
271
|
+
if (value.isWholeNumber()) {
|
|
272
|
+
// whole numbers are tricky - they have implied infinite precision, but we need to check for digit errors in the last couple of digits
|
|
273
|
+
if (res.checkForCouldBeWholeNumber) {
|
|
274
|
+
res.checkForCouldBeWholeNumber();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return res;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get all properties from defined units
|
|
283
|
+
* @return {Set<string>} set of properties
|
|
284
|
+
*/
|
|
285
|
+
getProperties() {
|
|
286
|
+
if (!this.model) {
|
|
287
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const result = new Set();
|
|
291
|
+
for (const unit of this.model.getDefinedUnits()) {
|
|
292
|
+
if (unit.property) {
|
|
293
|
+
result.add(unit.property);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get all prefixes
|
|
301
|
+
* @return {Array} array of prefixes
|
|
302
|
+
*/
|
|
303
|
+
getPrefixes() {
|
|
304
|
+
if (!this.model) {
|
|
305
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return this.model.getPrefixes();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get all base units
|
|
313
|
+
* @return {Array} array of base units
|
|
314
|
+
*/
|
|
315
|
+
getBaseUnits() {
|
|
316
|
+
if (!this.model) {
|
|
317
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return this.model.getBaseUnits();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get all defined units
|
|
325
|
+
* @return {Array} array of defined units
|
|
326
|
+
*/
|
|
327
|
+
getDefinedUnits() {
|
|
328
|
+
if (!this.model) {
|
|
329
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return this.model.getDefinedUnits();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get the UCUM model
|
|
337
|
+
* @return {UcumModel} the loaded model
|
|
338
|
+
*/
|
|
339
|
+
getModel() {
|
|
340
|
+
return this.model;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Get the handlers registry
|
|
345
|
+
* @return {Registry} the handlers registry
|
|
346
|
+
*/
|
|
347
|
+
getHandlers() {
|
|
348
|
+
return this.handlers;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Search for concepts in the model
|
|
353
|
+
* @param {ConceptKind} kind - type of concept to search for (optional)
|
|
354
|
+
* @param {string} text - text to search for
|
|
355
|
+
* @param {boolean} isRegex - whether text is a regex pattern
|
|
356
|
+
* @return {Array} array of matching concepts
|
|
357
|
+
*/
|
|
358
|
+
search(kind = null, text = '', isRegex = false) {
|
|
359
|
+
if (!this.model) {
|
|
360
|
+
throw new UcumException("UCUM service not initialized - call init() first");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const search = new Search();
|
|
364
|
+
return search.doSearch(this.model, kind, text, isRegex);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
module.exports = {
|
|
369
|
+
UcumService
|
|
370
|
+
};
|