@ovok/core 0.4.36 → 0.4.38
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/dist/client/auth/account-storage.d.ts +1 -0
- package/dist/client/auth/account-storage.js +2 -1
- package/dist/{utils → client}/auth/exchange-code.d.ts +1 -1
- package/dist/{utils → client}/auth/generate-code-challenge-and-verifier.js +5 -17
- package/dist/{utils → client}/auth/handle-tenant-code.d.ts +1 -1
- package/dist/{utils → client}/auth/handle-tenant-code.js +1 -1
- package/dist/client/auth/methods/appleLogin.js +2 -6
- package/dist/client/auth/methods/changePassword.js +2 -1
- package/dist/client/auth/methods/getSessions.js +2 -1
- package/dist/client/auth/methods/googleLogin.js +2 -6
- package/dist/client/auth/methods/login.js +26 -44
- package/dist/client/auth/methods/logout.js +2 -1
- package/dist/client/auth/methods/requestDeleteUser.js +5 -4
- package/dist/client/auth/methods/revokeSessions.js +2 -1
- package/dist/client/auth/methods/socialLogin.d.ts +7 -0
- package/dist/client/auth/methods/socialLogin.js +11 -0
- package/dist/client/auth/session-fetch.d.ts +35 -0
- package/dist/client/auth/session-fetch.js +206 -0
- package/dist/client/auth/session-http.d.ts +11 -0
- package/dist/client/auth/session-http.js +169 -0
- package/dist/client/auth/sha256.d.ts +2 -0
- package/dist/client/auth/sha256.js +65 -0
- package/dist/{utils → client}/auth/start-login.d.ts +2 -2
- package/dist/client/change-history/methods.js +2 -1
- package/dist/client/cms/methods.js +16 -28
- package/dist/client/consent/methods.js +3 -2
- package/dist/client/email/methods.js +2 -1
- package/dist/client/errors/ovok-error-utils.d.ts +6 -0
- package/dist/client/errors/ovok-error-utils.js +10 -1
- package/dist/client/errors/ovok-error.js +6 -13
- package/dist/client/errors/rate-limit-error.js +2 -6
- package/dist/client/experience/methods.js +28 -37
- package/dist/client/invites/methods.js +8 -12
- package/dist/client/notifications/methods.js +5 -5
- package/dist/client/observation/fhir-systems.d.ts +4 -0
- package/dist/client/observation/fhir-systems.js +7 -0
- package/dist/client/observation/methods/generateObservationBodiesByMeasurement.js +23 -29
- package/dist/client/observation/methods/getLatestObservationsByCodes.js +6 -9
- package/dist/client/observation/methods/observationsToMeasurements.js +44 -91
- package/dist/client/observation/services/base-observation-service.js +4 -3
- package/dist/client/observation/services/blood-pressure.js +1 -4
- package/dist/client/observation/services/{baby-sleep.d.ts → component-observation-services.d.ts} +18 -13
- package/dist/client/observation/services/component-observation-services.js +69 -0
- package/dist/client/observation/services/creator.d.ts +1 -2
- package/dist/client/observation/services/creator.js +15 -33
- package/dist/client/observation/services/ecg-observation-fragments.js +5 -6
- package/dist/client/observation/services/ecg.js +3 -2
- package/dist/client/observation/services/lipid-panel.js +8 -12
- package/dist/client/observation/services/measurement-time.d.ts +4 -0
- package/dist/client/observation/services/measurement-time.js +6 -0
- package/dist/client/observation/services/single-value-services.d.ts +36 -0
- package/dist/client/observation/services/single-value-services.js +100 -0
- package/dist/client/observation/services/uric-acid-service.js +6 -7
- package/dist/client/observation/types/generate-measurement/HealthConnectTypes.d.ts +2 -458
- package/dist/client/observation/types/generate-measurement/HealthConnectTypes.js +0 -6
- package/dist/client/offline/offline-measurement-queue.js +31 -28
- package/dist/client/ovok-client.d.ts +7 -15
- package/dist/client/ovok-client.js +54 -398
- package/dist/client/patient/methods.js +27 -36
- package/dist/client/patient-sharing/methods.js +4 -4
- package/dist/client/platform/methods.js +21 -30
- package/dist/client/platform/types.d.ts +3 -6
- package/dist/client/query-string.d.ts +3 -0
- package/dist/client/query-string.js +15 -0
- package/dist/client/questionnaire-response/methods/createQuestionnaireResponseWithObservations.js +2 -1
- package/dist/client/questionnaire-response/methods/extractQuestionnaireObservations.js +1 -2
- package/dist/client/questionnaire-response/methods/populateQuestionnaire.js +1 -2
- package/dist/client/questionnaire-response/methods/submitQuestionnaireResponse.js +2 -1
- package/dist/client/request-options.d.ts +2 -0
- package/dist/client/request-options.js +6 -0
- package/dist/client/signals/errors.js +2 -6
- package/dist/client/signals/methods.js +22 -22
- package/dist/client/slim/live-read.d.ts +4 -0
- package/dist/client/slim/live-read.js +87 -0
- package/dist/client/slim/methods.d.ts +3 -0
- package/dist/client/slim/methods.js +73 -166
- package/dist/client/storage.js +3 -2
- package/dist/conformance/capability-requirements.js +1 -1
- package/dist/conformance/capability.js +8 -7
- package/dist/hooks/account-hooks.js +6 -68
- package/dist/hooks/api-hooks.d.ts +10 -6
- package/dist/hooks/api-hooks.js +29 -88
- package/dist/hooks/cms-hooks.js +8 -13
- package/dist/hooks/email-hooks.js +1 -40
- package/dist/hooks/experience-hooks.js +4 -60
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +4 -4
- package/dist/hooks/invites-hooks.js +0 -1
- package/dist/hooks/notification-hooks.d.ts +4 -1
- package/dist/hooks/notification-hooks.js +29 -86
- package/dist/hooks/notification-invalidation.d.ts +6 -0
- package/dist/hooks/notification-invalidation.js +52 -0
- package/dist/hooks/notification-note-hooks.js +13 -28
- package/dist/hooks/observation-hooks.js +2 -4
- package/dist/hooks/patient-hooks.js +13 -113
- package/dist/hooks/patient-sharing-hooks.js +2 -7
- package/dist/hooks/platform-hooks.js +3 -51
- package/dist/hooks/poll-hooks.js +10 -8
- package/dist/hooks/signals-alert-hooks.js +4 -6
- package/dist/hooks/signals-hooks.js +2 -45
- package/package.json +1 -1
- package/dist/client/observation/services/baby-sleep.js +0 -69
- package/dist/client/observation/services/blood-glucose.d.ts +0 -12
- package/dist/client/observation/services/blood-glucose.js +0 -36
- package/dist/client/observation/services/body-temperature.d.ts +0 -13
- package/dist/client/observation/services/body-temperature.js +0 -36
- package/dist/client/observation/services/body-weight.d.ts +0 -13
- package/dist/client/observation/services/body-weight.js +0 -36
- package/dist/client/observation/services/heart-rate-variability.d.ts +0 -13
- package/dist/client/observation/services/heart-rate-variability.js +0 -38
- package/dist/client/observation/services/heart-rate.d.ts +0 -13
- package/dist/client/observation/services/heart-rate.js +0 -39
- package/dist/client/observation/services/respiratory-rate.d.ts +0 -13
- package/dist/client/observation/services/respiratory-rate.js +0 -38
- package/dist/client/observation/services/respiratory-therapy.d.ts +0 -28
- package/dist/client/observation/services/respiratory-therapy.js +0 -69
- package/dist/client/observation/services/resting-heart-rate.d.ts +0 -13
- package/dist/client/observation/services/resting-heart-rate.js +0 -38
- package/dist/client/observation/services/vo2-max.d.ts +0 -13
- package/dist/client/observation/services/vo2-max.js +0 -38
- package/dist/client/observation/services/walking-heart-rate-average.d.ts +0 -12
- package/dist/client/observation/services/walking-heart-rate-average.js +0 -38
- package/dist/utils/auth/sha256.d.ts +0 -8
- package/dist/utils/auth/sha256.js +0 -300
- /package/dist/{utils → client}/auth/exchange-code.js +0 -0
- /package/dist/{utils → client}/auth/generate-code-challenge-and-verifier.d.ts +0 -0
- /package/dist/{utils → client}/auth/start-login.js +0 -0
|
@@ -5,6 +5,7 @@ exports.invitePatient = invitePatient;
|
|
|
5
5
|
exports.acceptPractitionerInvite = acceptPractitionerInvite;
|
|
6
6
|
exports.acceptPatientInvite = acceptPatientInvite;
|
|
7
7
|
exports.acceptInvite = acceptInvite;
|
|
8
|
+
const request_options_1 = require("../request-options");
|
|
8
9
|
function invitePractitioner(body, requestOptions) {
|
|
9
10
|
return this.post("/v1/invites/practitioner", body, undefined, requestOptions);
|
|
10
11
|
}
|
|
@@ -20,17 +21,12 @@ function acceptPatientInvite(body, requestOptions) {
|
|
|
20
21
|
function acceptInvite(body, requestOptions) {
|
|
21
22
|
var _a;
|
|
22
23
|
const resourceType = (_a = this.getProfile()) === null || _a === void 0 ? void 0 : _a.resourceType;
|
|
23
|
-
if (resourceType
|
|
24
|
-
|
|
25
|
-
this.invalidateUrl("/v1/patient-sharing/shares");
|
|
26
|
-
return result;
|
|
27
|
-
});
|
|
24
|
+
if (resourceType !== "Practitioner" && resourceType !== "Patient") {
|
|
25
|
+
throw new Error("Accepting an invite requires a signed-in Patient or Practitioner profile.");
|
|
28
26
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
throw new Error("Accepting an invite requires a signed-in Patient or Practitioner profile.");
|
|
27
|
+
const accept = resourceType === "Practitioner" ? this.acceptPractitionerInvite : this.acceptPatientInvite;
|
|
28
|
+
return accept.call(this, body, ...(0, request_options_1.optionalArg)(requestOptions)).then((result) => {
|
|
29
|
+
this.invalidateUrl("/v1/patient-sharing/shares");
|
|
30
|
+
return result;
|
|
31
|
+
});
|
|
36
32
|
}
|
|
@@ -5,7 +5,7 @@ exports.listNotificationNotes = listNotificationNotes;
|
|
|
5
5
|
exports.updateNotificationNote = updateNotificationNote;
|
|
6
6
|
exports.patchNotificationNote = patchNotificationNote;
|
|
7
7
|
exports.deleteNotificationNote = deleteNotificationNote;
|
|
8
|
-
const
|
|
8
|
+
const request_options_1 = require("../request-options");
|
|
9
9
|
const TARGET_PATH = {
|
|
10
10
|
oob: "oob",
|
|
11
11
|
alarm: "alarms",
|
|
@@ -16,14 +16,14 @@ function createNotificationNote(body, requestOptions) {
|
|
|
16
16
|
return this.post("/v1/slim/notifications/notes", body, undefined, requestOptions);
|
|
17
17
|
}
|
|
18
18
|
function listNotificationNotes(kind, id, requestOptions) {
|
|
19
|
-
return this.get(`/v1/slim/notifications/${TARGET_PATH[kind]}/${
|
|
19
|
+
return this.get(`/v1/slim/notifications/${TARGET_PATH[kind]}/${encodeURIComponent(id)}/notes`, { cache: "reload", ...requestOptions });
|
|
20
20
|
}
|
|
21
21
|
function updateNotificationNote(noteId, body, requestOptions) {
|
|
22
|
-
return this.put(`/v1/slim/notifications/notes/${
|
|
22
|
+
return this.put(`/v1/slim/notifications/notes/${encodeURIComponent(noteId)}`, body, undefined, requestOptions);
|
|
23
23
|
}
|
|
24
24
|
function patchNotificationNote(noteId, body, requestOptions) {
|
|
25
|
-
return this.patch(`/v1/slim/notifications/notes/${
|
|
25
|
+
return this.patch(`/v1/slim/notifications/notes/${encodeURIComponent(noteId)}`, body, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
26
26
|
}
|
|
27
27
|
async function deleteNotificationNote(noteId, requestOptions) {
|
|
28
|
-
await this.delete(`/v1/slim/notifications/notes/${
|
|
28
|
+
await this.delete(`/v1/slim/notifications/notes/${encodeURIComponent(noteId)}`, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
29
29
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const LOINC_SYSTEM = "http://loinc.org";
|
|
2
|
+
export declare const UCUM_SYSTEM = "http://unitsofmeasure.org";
|
|
3
|
+
/** Identifier system of the device-side reading id that makes a save idempotent. */
|
|
4
|
+
export declare const DEVICE_READING_IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/device-reading-id";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEVICE_READING_IDENTIFIER_SYSTEM = exports.UCUM_SYSTEM = exports.LOINC_SYSTEM = void 0;
|
|
4
|
+
exports.LOINC_SYSTEM = "http://loinc.org";
|
|
5
|
+
exports.UCUM_SYSTEM = "http://unitsofmeasure.org";
|
|
6
|
+
/** Identifier system of the device-side reading id that makes a save idempotent. */
|
|
7
|
+
exports.DEVICE_READING_IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/device-reading-id";
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateObservationBodiesByMeasurement = generateObservationBodiesByMeasurement;
|
|
4
4
|
const core_1 = require("@medplum/core");
|
|
5
|
+
const fhir_systems_1 = require("../fhir-systems");
|
|
5
6
|
const creator_1 = require("../services/creator");
|
|
6
7
|
const getDeviceObject_1 = require("../utils/getDeviceObject");
|
|
7
|
-
const DEVICE_READING_IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/device-reading-id";
|
|
8
8
|
const getPatientReference = async (client, patientId) => {
|
|
9
9
|
if (patientId) {
|
|
10
10
|
return `Patient/${patientId}`;
|
|
@@ -20,7 +20,7 @@ const addSourceIdentifier = (observations, patientReference, sourceId) => patien
|
|
|
20
20
|
identifier: [
|
|
21
21
|
...((_a = observation.identifier) !== null && _a !== void 0 ? _a : []),
|
|
22
22
|
{
|
|
23
|
-
system: DEVICE_READING_IDENTIFIER_SYSTEM,
|
|
23
|
+
system: fhir_systems_1.DEVICE_READING_IDENTIFIER_SYSTEM,
|
|
24
24
|
value: `${patientReference}/${sourceId}/${index}`,
|
|
25
25
|
},
|
|
26
26
|
],
|
|
@@ -28,52 +28,46 @@ const addSourceIdentifier = (observations, patientReference, sourceId) => patien
|
|
|
28
28
|
})
|
|
29
29
|
: observations;
|
|
30
30
|
async function generateObservationBodiesByMeasurement(params) {
|
|
31
|
-
var _a;
|
|
31
|
+
var _a, _b;
|
|
32
32
|
const { device, measurement } = params;
|
|
33
33
|
const measurementTypeKey = measurement.measurementTypeKey;
|
|
34
34
|
const patientReference = await getPatientReference(this, params.patientId);
|
|
35
35
|
const observationService = creator_1.ObservationServiceCreator.getInstance().getService(measurementTypeKey);
|
|
36
36
|
const deviceObject = (0, getDeviceObject_1.getDeviceObject)(device);
|
|
37
|
-
const observationFragments = (_a = observationService.generateObservationFragments) === null || _a === void 0 ? void 0 : _a.call(observationService, measurement);
|
|
38
37
|
const applyEffectiveDateTime = (observation) => params.effectiveDateTime && !observation.effectivePeriod
|
|
39
38
|
? {
|
|
40
39
|
...observation,
|
|
41
40
|
effectiveDateTime: params.effectiveDateTime.toISOString(),
|
|
42
41
|
}
|
|
43
42
|
: observation;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
resourceType: "Observation",
|
|
48
|
-
status: "registered",
|
|
49
|
-
subject: {
|
|
50
|
-
reference: patientReference,
|
|
51
|
-
},
|
|
52
|
-
device: deviceObject,
|
|
53
|
-
...fragment.value,
|
|
54
|
-
code: fragment.code,
|
|
55
|
-
...(fragment.additionalProperties || {}),
|
|
56
|
-
}))
|
|
57
|
-
.filter(hasObservationValue)
|
|
58
|
-
.map(applyEffectiveDateTime);
|
|
59
|
-
return addSourceIdentifier(observations, patientReference, params.sourceId);
|
|
60
|
-
}
|
|
61
|
-
const observationCodes = observationService.getObservationCodes();
|
|
62
|
-
const values = observationService.generateValues(measurement);
|
|
63
|
-
const additionalProperties = observationService.getAdditionalProperties(measurement);
|
|
64
|
-
const observationBodies = observationCodes.map((code, index) => ({
|
|
43
|
+
const fragments = (_b = (_a = observationService.generateObservationFragments) === null || _a === void 0 ? void 0 : _a.call(observationService, measurement)) !== null && _b !== void 0 ? _b : codeValueFragments(observationService, measurement);
|
|
44
|
+
const observations = fragments
|
|
45
|
+
.map((fragment) => ({
|
|
65
46
|
resourceType: "Observation",
|
|
66
47
|
status: "registered",
|
|
67
48
|
subject: {
|
|
68
49
|
reference: patientReference,
|
|
69
50
|
},
|
|
70
51
|
device: deviceObject,
|
|
71
|
-
...
|
|
52
|
+
...fragment.value,
|
|
53
|
+
code: fragment.code,
|
|
54
|
+
...(fragment.additionalProperties || {}),
|
|
55
|
+
}))
|
|
56
|
+
.filter(hasObservationValue)
|
|
57
|
+
.map(applyEffectiveDateTime);
|
|
58
|
+
return addSourceIdentifier(observations, patientReference, params.sourceId);
|
|
59
|
+
}
|
|
60
|
+
/** Services without fragments describe their observations as parallel code, value and property lists. */
|
|
61
|
+
const codeValueFragments = (service, measurement) => {
|
|
62
|
+
const codes = service.getObservationCodes();
|
|
63
|
+
const values = service.generateValues(measurement);
|
|
64
|
+
const additionalProperties = service.getAdditionalProperties(measurement);
|
|
65
|
+
return codes.map((code, index) => ({
|
|
72
66
|
code,
|
|
73
|
-
|
|
67
|
+
value: values[index],
|
|
68
|
+
additionalProperties: additionalProperties[index],
|
|
74
69
|
}));
|
|
75
|
-
|
|
76
|
-
}
|
|
70
|
+
};
|
|
77
71
|
const hasObservationValue = (observation) => {
|
|
78
72
|
var _a, _b, _c, _d, _e, _f;
|
|
79
73
|
return ((_a = observation.valueQuantity) === null || _a === void 0 ? void 0 : _a.value) !== undefined ||
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getLatestObservationsByCodes = getLatestObservationsByCodes;
|
|
4
|
+
const query_string_1 = require("../../query-string");
|
|
4
5
|
async function getLatestObservationsByCodes(params, requestOptions) {
|
|
5
6
|
var _a;
|
|
6
7
|
let patientId = "patientId" in params ? params.patientId : "";
|
|
@@ -8,14 +9,10 @@ async function getLatestObservationsByCodes(params, requestOptions) {
|
|
|
8
9
|
const currentUser = await this.getProfileAsync();
|
|
9
10
|
patientId = (_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.id) !== null && _a !== void 0 ? _a : "";
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
code: codes,
|
|
12
|
+
return this.get(`/fhir/Observation/$lastn${(0, query_string_1.toQueryString)({
|
|
13
|
+
max: 1,
|
|
14
|
+
code: params.codes,
|
|
15
15
|
patient: `Patient/${patientId}`,
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
return this.get(`/fhir/Observation/$lastn?${urlParams.toString()}`, { ...{
|
|
19
|
-
cache: "no-cache",
|
|
20
|
-
}, ...requestOptions });
|
|
16
|
+
date: params.date ? `ge${params.date}` : undefined,
|
|
17
|
+
})}`, { cache: "no-cache", ...requestOptions });
|
|
21
18
|
}
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.observationsToMeasurements = exports.observationCodesForTypes = void 0;
|
|
5
5
|
const types_1 = require("../../../types");
|
|
6
|
+
const fhir_systems_1 = require("../fhir-systems");
|
|
7
|
+
const single_value_services_1 = require("../services/single-value-services");
|
|
6
8
|
const DEFAULT_OBSERVATION_TIME_TOLERANCE_MS = 1000;
|
|
7
9
|
const codeOf = (observation) => {
|
|
8
10
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
9
|
-
return (_d = (_c = (_b = (_a = observation.code) === null || _a === void 0 ? void 0 : _a.coding) === null || _b === void 0 ? void 0 : _b.find((coding) => coding.system ===
|
|
11
|
+
return (_d = (_c = (_b = (_a = observation.code) === null || _a === void 0 ? void 0 : _a.coding) === null || _b === void 0 ? void 0 : _b.find((coding) => coding.system === fhir_systems_1.LOINC_SYSTEM)) === null || _c === void 0 ? void 0 : _c.code) !== null && _d !== void 0 ? _d : (_g = (_f = (_e = observation.code) === null || _e === void 0 ? void 0 : _e.coding) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.code;
|
|
10
12
|
};
|
|
11
13
|
const valueOf = (observation) => {
|
|
12
14
|
var _a;
|
|
@@ -199,9 +201,10 @@ const groupObservations = (observations, options, legacyKey) => {
|
|
|
199
201
|
}
|
|
200
202
|
return [...groups.values()].sort((left, right) => left.firstIndex - right.firstIndex);
|
|
201
203
|
};
|
|
202
|
-
const firstValue = (observations, code) =>
|
|
203
|
-
|
|
204
|
-
: undefined;
|
|
204
|
+
const firstValue = (observations, code) => {
|
|
205
|
+
const match = observations.find((observation) => codeOf(observation) === code);
|
|
206
|
+
return match ? valueOf(match) : undefined;
|
|
207
|
+
};
|
|
205
208
|
const LIPID_FIELDS = [
|
|
206
209
|
{ key: "totalCholesterol", codes: [types_1.ObservationCode.TOTAL_CHOLESTEROL_MASS, types_1.ObservationCode.TOTAL_CHOLESTEROL_MOLAR] },
|
|
207
210
|
{ key: "hdlCholesterol", codes: [types_1.ObservationCode.HDL_CHOLESTEROL_MASS, types_1.ObservationCode.HDL_CHOLESTEROL_MOLAR] },
|
|
@@ -220,40 +223,45 @@ const lipidValue = (observations, field) => {
|
|
|
220
223
|
}
|
|
221
224
|
return undefined;
|
|
222
225
|
};
|
|
226
|
+
const MOLAR_LIPID_CODES = [
|
|
227
|
+
types_1.ObservationCode.TOTAL_CHOLESTEROL_MOLAR,
|
|
228
|
+
types_1.ObservationCode.HDL_CHOLESTEROL_MOLAR,
|
|
229
|
+
types_1.ObservationCode.LDL_CHOLESTEROL_MOLAR,
|
|
230
|
+
types_1.ObservationCode.TRIGLYCERIDES_MOLAR,
|
|
231
|
+
];
|
|
232
|
+
const MASS_LIPID_CODES = [
|
|
233
|
+
types_1.ObservationCode.TOTAL_CHOLESTEROL_MASS,
|
|
234
|
+
types_1.ObservationCode.HDL_CHOLESTEROL_MASS,
|
|
235
|
+
types_1.ObservationCode.LDL_CHOLESTEROL_MASS,
|
|
236
|
+
types_1.ObservationCode.TRIGLYCERIDES_MASS,
|
|
237
|
+
];
|
|
238
|
+
/** The unit a lipid code implies, else the quantity's own unit when it is a lipid unit. */
|
|
239
|
+
const lipidUnitOf = (code, unit) => {
|
|
240
|
+
if (code && MOLAR_LIPID_CODES.includes(code)) {
|
|
241
|
+
return "mmol/L";
|
|
242
|
+
}
|
|
243
|
+
if (code && MASS_LIPID_CODES.includes(code)) {
|
|
244
|
+
return "mg/dL";
|
|
245
|
+
}
|
|
246
|
+
return unit === "mmol/L" || unit === "mg/dL" ? unit : undefined;
|
|
247
|
+
};
|
|
223
248
|
const lipidUnit = (observations) => {
|
|
224
|
-
var _a, _b, _c, _d, _e
|
|
249
|
+
var _a, _b, _c, _d, _e;
|
|
225
250
|
for (const observation of observations) {
|
|
226
251
|
for (const component of (_a = observation.component) !== null && _a !== void 0 ? _a : []) {
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
return "mmol/L";
|
|
252
|
+
const unit = lipidUnitOf((_c = (_b = component.code.coding) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.code, (_d = component.valueQuantity) === null || _d === void 0 ? void 0 : _d.unit);
|
|
253
|
+
if (unit) {
|
|
254
|
+
return unit;
|
|
231
255
|
}
|
|
232
|
-
if ([types_1.ObservationCode.TOTAL_CHOLESTEROL_MASS, types_1.ObservationCode.HDL_CHOLESTEROL_MASS,
|
|
233
|
-
types_1.ObservationCode.LDL_CHOLESTEROL_MASS, types_1.ObservationCode.TRIGLYCERIDES_MASS].includes(code)) {
|
|
234
|
-
return "mg/dL";
|
|
235
|
-
}
|
|
236
|
-
if (((_d = component.valueQuantity) === null || _d === void 0 ? void 0 : _d.unit) === "mmol/L" || ((_e = component.valueQuantity) === null || _e === void 0 ? void 0 : _e.unit) === "mg/dL") {
|
|
237
|
-
return component.valueQuantity.unit;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
const code = codeOf(observation);
|
|
241
|
-
const unit = (_f = observation.valueQuantity) === null || _f === void 0 ? void 0 : _f.unit;
|
|
242
|
-
if (code && [types_1.ObservationCode.TOTAL_CHOLESTEROL_MOLAR, types_1.ObservationCode.HDL_CHOLESTEROL_MOLAR,
|
|
243
|
-
types_1.ObservationCode.LDL_CHOLESTEROL_MOLAR, types_1.ObservationCode.TRIGLYCERIDES_MOLAR].includes(code)) {
|
|
244
|
-
return "mmol/L";
|
|
245
256
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return "mg/dL";
|
|
249
|
-
}
|
|
250
|
-
if (unit === "mmol/L" || unit === "mg/dL") {
|
|
257
|
+
const unit = lipidUnitOf(codeOf(observation), (_e = observation.valueQuantity) === null || _e === void 0 ? void 0 : _e.unit);
|
|
258
|
+
if (unit) {
|
|
251
259
|
return unit;
|
|
252
260
|
}
|
|
253
261
|
}
|
|
254
262
|
return "mg/dL";
|
|
255
263
|
};
|
|
256
|
-
const
|
|
264
|
+
const componentsOf = (observations) => observations.flatMap((observation) => { var _a; return (_a = observation.component) !== null && _a !== void 0 ? _a : []; });
|
|
257
265
|
const baseMeasurement = (measurementTypeKey, observation) => ({
|
|
258
266
|
measurementTypeKey,
|
|
259
267
|
...(observation.id ? { id: observation.id } : {}),
|
|
@@ -269,6 +277,10 @@ const inverseGroup = (type, observations) => {
|
|
|
269
277
|
})[0];
|
|
270
278
|
const measurement = baseMeasurement(type, first);
|
|
271
279
|
const value = (code) => firstValue(observations, code);
|
|
280
|
+
const single = single_value_services_1.singleValueFields.get(type);
|
|
281
|
+
if (single) {
|
|
282
|
+
return { ...measurement, measurementTypeKey: type, [single.valueKey]: value(single.code) };
|
|
283
|
+
}
|
|
272
284
|
switch (type) {
|
|
273
285
|
case types_1.MeasurementTypeKey.bloodPressure:
|
|
274
286
|
return {
|
|
@@ -280,12 +292,6 @@ const inverseGroup = (type, observations) => {
|
|
|
280
292
|
heartRate: value(types_1.ObservationCode.BLOOD_PRESSURE_HEART_RATE),
|
|
281
293
|
irregularHeartbeat: irregularHeartbeatOf(observations),
|
|
282
294
|
};
|
|
283
|
-
case types_1.MeasurementTypeKey.bloodGlucose:
|
|
284
|
-
return { ...measurement, measurementTypeKey: type, bloodGlucose: value(types_1.ObservationCode.BLOOD_GLUCOSE) };
|
|
285
|
-
case types_1.MeasurementTypeKey.bodyWeight:
|
|
286
|
-
return { ...measurement, measurementTypeKey: type, bodyWeight: value(types_1.ObservationCode.BODY_WEIGHT) };
|
|
287
|
-
case types_1.MeasurementTypeKey.temperature:
|
|
288
|
-
return { ...measurement, measurementTypeKey: type, bodyTemp: value(types_1.ObservationCode.BODY_TEMPERATURE) };
|
|
289
295
|
case types_1.MeasurementTypeKey.bloodOxygenPulseRate:
|
|
290
296
|
return {
|
|
291
297
|
...measurement,
|
|
@@ -293,42 +299,6 @@ const inverseGroup = (type, observations) => {
|
|
|
293
299
|
bloodOxygen: value(types_1.ObservationCode.OXYGEN_SATURATION),
|
|
294
300
|
pulseRate: value(types_1.ObservationCode.OXYGEN_SATURATION_HEART_RATE),
|
|
295
301
|
};
|
|
296
|
-
case types_1.MeasurementTypeKey.heartRate:
|
|
297
|
-
return {
|
|
298
|
-
...measurement,
|
|
299
|
-
measurementTypeKey: type,
|
|
300
|
-
heartRate: value(types_1.ObservationCode.HEART_RATE),
|
|
301
|
-
};
|
|
302
|
-
case types_1.MeasurementTypeKey.heartRateVariability:
|
|
303
|
-
return {
|
|
304
|
-
...measurement,
|
|
305
|
-
measurementTypeKey: type,
|
|
306
|
-
heartRateVariability: value(types_1.ObservationCode.HEART_RATE_VARIABILITY),
|
|
307
|
-
};
|
|
308
|
-
case types_1.MeasurementTypeKey.respiratoryRate:
|
|
309
|
-
return {
|
|
310
|
-
...measurement,
|
|
311
|
-
measurementTypeKey: type,
|
|
312
|
-
respiratoryRate: value(types_1.ObservationCode.RESPIRATORY_RATE),
|
|
313
|
-
};
|
|
314
|
-
case types_1.MeasurementTypeKey.restingHeartRate:
|
|
315
|
-
return {
|
|
316
|
-
...measurement,
|
|
317
|
-
measurementTypeKey: type,
|
|
318
|
-
restingHeartRate: value(types_1.ObservationCode.RESTING_HEART_RATE),
|
|
319
|
-
};
|
|
320
|
-
case types_1.MeasurementTypeKey.walkingHeartRateAverage:
|
|
321
|
-
return {
|
|
322
|
-
...measurement,
|
|
323
|
-
measurementTypeKey: type,
|
|
324
|
-
walkingHeartRateAverage: value(types_1.ObservationCode.WALKING_HEART_RATE_AVERAGE),
|
|
325
|
-
};
|
|
326
|
-
case types_1.MeasurementTypeKey.vo2Max:
|
|
327
|
-
return {
|
|
328
|
-
...measurement,
|
|
329
|
-
measurementTypeKey: type,
|
|
330
|
-
vo2Max: value(types_1.ObservationCode.VO2_MAX),
|
|
331
|
-
};
|
|
332
302
|
case types_1.MeasurementTypeKey.stepCount:
|
|
333
303
|
return {
|
|
334
304
|
...measurement,
|
|
@@ -395,34 +365,22 @@ const inverseGroup = (type, observations) => {
|
|
|
395
365
|
})),
|
|
396
366
|
};
|
|
397
367
|
case types_1.MeasurementTypeKey.respiratoryTherapy:
|
|
398
|
-
return {
|
|
399
|
-
...measurement,
|
|
400
|
-
measurementTypeKey: type,
|
|
401
|
-
components: respiratoryComponents(observations),
|
|
402
|
-
};
|
|
403
368
|
case types_1.MeasurementTypeKey.babySleep:
|
|
404
369
|
return {
|
|
405
370
|
...measurement,
|
|
406
371
|
measurementTypeKey: type,
|
|
407
|
-
components:
|
|
372
|
+
components: componentsOf(observations),
|
|
408
373
|
};
|
|
409
374
|
default:
|
|
410
375
|
return measurement;
|
|
411
376
|
}
|
|
412
377
|
};
|
|
378
|
+
const COMPONENT_TYPES = new Set([types_1.MeasurementTypeKey.respiratoryTherapy, types_1.MeasurementTypeKey.babySleep]);
|
|
413
379
|
const codesForType = {
|
|
380
|
+
...Object.fromEntries([...single_value_services_1.singleValueFields].map(([type, { code }]) => [type, [code]])),
|
|
414
381
|
[types_1.MeasurementTypeKey.bloodPressure]: [types_1.ObservationCode.SYSTOLIC_BLOOD_PRESSURE, types_1.ObservationCode.DIASTOLIC_BLOOD_PRESSURE, types_1.ObservationCode.MEAN_BLOOD_PRESSURE, types_1.ObservationCode.BLOOD_PRESSURE_HEART_RATE, types_1.PulseRhythmCoding.observation],
|
|
415
|
-
[types_1.MeasurementTypeKey.bloodGlucose]: [types_1.ObservationCode.BLOOD_GLUCOSE],
|
|
416
|
-
[types_1.MeasurementTypeKey.bodyWeight]: [types_1.ObservationCode.BODY_WEIGHT],
|
|
417
|
-
[types_1.MeasurementTypeKey.temperature]: [types_1.ObservationCode.BODY_TEMPERATURE],
|
|
418
382
|
[types_1.MeasurementTypeKey.bloodOxygenPulseRate]: [types_1.ObservationCode.OXYGEN_SATURATION, types_1.ObservationCode.OXYGEN_SATURATION_HEART_RATE],
|
|
419
383
|
[types_1.MeasurementTypeKey.stepCount]: [types_1.ObservationCode.STEPS],
|
|
420
|
-
[types_1.MeasurementTypeKey.heartRate]: [types_1.ObservationCode.HEART_RATE],
|
|
421
|
-
[types_1.MeasurementTypeKey.heartRateVariability]: [types_1.ObservationCode.HEART_RATE_VARIABILITY],
|
|
422
|
-
[types_1.MeasurementTypeKey.respiratoryRate]: [types_1.ObservationCode.RESPIRATORY_RATE],
|
|
423
|
-
[types_1.MeasurementTypeKey.restingHeartRate]: [types_1.ObservationCode.RESTING_HEART_RATE],
|
|
424
|
-
[types_1.MeasurementTypeKey.walkingHeartRateAverage]: [types_1.ObservationCode.WALKING_HEART_RATE_AVERAGE],
|
|
425
|
-
[types_1.MeasurementTypeKey.vo2Max]: [types_1.ObservationCode.VO2_MAX],
|
|
426
384
|
[types_1.MeasurementTypeKey.ecg]: [types_1.ObservationCode.ECG, types_1.ObservationCode.ECG_HEART_RATE, types_1.ObservationCode.HEART_RATE],
|
|
427
385
|
[types_1.MeasurementTypeKey.uricAcid]: [
|
|
428
386
|
types_1.ObservationCode.URIC_ACID,
|
|
@@ -456,12 +414,7 @@ const observationsToMeasurements = (observations, types, options = {}) => {
|
|
|
456
414
|
!LIPID_FIELDS.some((field) => lipidValue(group.observations, field) !== undefined)) {
|
|
457
415
|
return;
|
|
458
416
|
}
|
|
459
|
-
if (type
|
|
460
|
-
respiratoryComponents(group.observations).length === 0) {
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
if (type === types_1.MeasurementTypeKey.babySleep &&
|
|
464
|
-
respiratoryComponents(group.observations).length === 0) {
|
|
417
|
+
if (COMPONENT_TYPES.has(type) && componentsOf(group.observations).length === 0) {
|
|
465
418
|
return;
|
|
466
419
|
}
|
|
467
420
|
output.push(inverseGroup(type, group.observations));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseObservationService = void 0;
|
|
4
|
+
const fhir_systems_1 = require("../fhir-systems");
|
|
4
5
|
class BaseObservationService {
|
|
5
6
|
getCastedMeasurement(measurement) {
|
|
6
7
|
if (measurement.measurementTypeKey !== this.measurementTypeKey) {
|
|
@@ -25,7 +26,7 @@ class BaseObservationService {
|
|
|
25
26
|
value: Number(fixedValue),
|
|
26
27
|
unit: metadata.unit,
|
|
27
28
|
code: metadata.primaryCode,
|
|
28
|
-
system:
|
|
29
|
+
system: fhir_systems_1.LOINC_SYSTEM,
|
|
29
30
|
},
|
|
30
31
|
};
|
|
31
32
|
});
|
|
@@ -36,12 +37,12 @@ class BaseObservationService {
|
|
|
36
37
|
{
|
|
37
38
|
code: metadata.primaryCode,
|
|
38
39
|
display: metadata.display,
|
|
39
|
-
system:
|
|
40
|
+
system: fhir_systems_1.LOINC_SYSTEM,
|
|
40
41
|
},
|
|
41
42
|
...metadata.additionalCodes.map((code) => ({
|
|
42
43
|
code,
|
|
43
44
|
display: metadata.display,
|
|
44
|
-
system:
|
|
45
|
+
system: fhir_systems_1.LOINC_SYSTEM,
|
|
45
46
|
})),
|
|
46
47
|
],
|
|
47
48
|
text: metadata.display,
|
|
@@ -35,10 +35,7 @@ class BloodPressureService extends base_observation_service_1.BaseObservationSer
|
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
primaryCode: types_1.ObservationCode.BLOOD_PRESSURE_HEART_RATE,
|
|
38
|
-
additionalCodes: [
|
|
39
|
-
types_1.ObservationCode.HEART_RATE,
|
|
40
|
-
types_1.ObservationCode.BLOOD_PRESSURE_HEART_RATE,
|
|
41
|
-
],
|
|
38
|
+
additionalCodes: [types_1.ObservationCode.HEART_RATE],
|
|
42
39
|
display: "Blood Pressure Heart Rate",
|
|
43
40
|
unit: "bpm",
|
|
44
41
|
valueKey: "heartRate",
|
package/dist/client/observation/services/{baby-sleep.d.ts → component-observation-services.d.ts}
RENAMED
|
@@ -1,28 +1,33 @@
|
|
|
1
|
-
import { Observation } from "@medplum/fhirtypes";
|
|
2
|
-
import {
|
|
1
|
+
import { CodeableConcept, Observation, ObservationComponent } from "@medplum/fhirtypes";
|
|
2
|
+
import { Measurement, MeasurementTypeKey, ObservationCode } from "../../../types";
|
|
3
3
|
import { RecordResult, RecordType } from "../types/generate-measurement/HealthConnectTypes";
|
|
4
4
|
import { Sample } from "../types/generate-measurement/Sample";
|
|
5
5
|
import { ObservationFragment } from "../types/generate-observation-body/ObservationFragment";
|
|
6
6
|
import { ObservationValue } from "../types/generate-observation-body/ObservationValue";
|
|
7
7
|
import { ObservationMetadata } from "../types/service/ObservationMetadata";
|
|
8
8
|
import { BaseObservationService } from "./base-observation-service";
|
|
9
|
-
|
|
9
|
+
type ComponentMeasurement = Measurement & {
|
|
10
|
+
components: ObservationComponent[];
|
|
11
|
+
};
|
|
12
|
+
/** One observation that carries the device adapter's coded components as they are. */
|
|
13
|
+
declare class ComponentObservationService extends BaseObservationService<ComponentMeasurement> {
|
|
10
14
|
protected measurementTypeKey: MeasurementTypeKey;
|
|
11
15
|
protected measurementTypeName: string;
|
|
12
|
-
|
|
16
|
+
private readonly code;
|
|
17
|
+
protected observationMetadata: ObservationMetadata<ComponentMeasurement>[];
|
|
18
|
+
private readonly title;
|
|
19
|
+
constructor(measurementTypeKey: MeasurementTypeKey, measurementTypeName: string, code: ObservationCode);
|
|
13
20
|
generateObservationFragments(measurement: Measurement): ObservationFragment[];
|
|
14
|
-
getObservationCodes():
|
|
15
|
-
coding: {
|
|
16
|
-
code: ObservationCode;
|
|
17
|
-
display: string;
|
|
18
|
-
system: string;
|
|
19
|
-
}[];
|
|
20
|
-
}[];
|
|
21
|
+
getObservationCodes(): CodeableConcept[];
|
|
21
22
|
getAdditionalProperties(measurement: Measurement): Partial<Observation>[];
|
|
22
23
|
generateValues(_measurement: Measurement): ObservationValue[];
|
|
23
24
|
getUnits(): string[];
|
|
24
25
|
getMeasurementDisplay(measurement: Measurement): string;
|
|
25
26
|
getObservationDisplay(observations: Observation[]): string | undefined;
|
|
26
|
-
convertSamplesToMeasurement(_samples: Sample[]):
|
|
27
|
-
convertRecordToMeasurement(_record: RecordResult<RecordType>):
|
|
27
|
+
convertSamplesToMeasurement(_samples: Sample[]): ComponentMeasurement;
|
|
28
|
+
convertRecordToMeasurement(_record: RecordResult<RecordType>): ComponentMeasurement;
|
|
29
|
+
private codeableConcept;
|
|
28
30
|
}
|
|
31
|
+
export declare const babySleepService: () => ComponentObservationService;
|
|
32
|
+
export declare const respiratoryTherapyService: () => ComponentObservationService;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.respiratoryTherapyService = exports.babySleepService = void 0;
|
|
4
|
+
const types_1 = require("../../../types");
|
|
5
|
+
const base_observation_service_1 = require("./base-observation-service");
|
|
6
|
+
const measurement_time_1 = require("./measurement-time");
|
|
7
|
+
const MEASUREMENT_TYPE_SYSTEM = "https://sdk.ovok.com/fhir/CodeSystem/measurement-type";
|
|
8
|
+
/** One observation that carries the device adapter's coded components as they are. */
|
|
9
|
+
class ComponentObservationService extends base_observation_service_1.BaseObservationService {
|
|
10
|
+
constructor(measurementTypeKey, measurementTypeName, code) {
|
|
11
|
+
super();
|
|
12
|
+
this.measurementTypeKey = measurementTypeKey;
|
|
13
|
+
this.measurementTypeName = measurementTypeName;
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.observationMetadata = [];
|
|
16
|
+
this.title = measurementTypeName.charAt(0).toUpperCase() + measurementTypeName.slice(1);
|
|
17
|
+
}
|
|
18
|
+
generateObservationFragments(measurement) {
|
|
19
|
+
const { components, recordedAt } = this.getCastedMeasurement(measurement);
|
|
20
|
+
if (components.length === 0) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return [{
|
|
24
|
+
code: this.codeableConcept(),
|
|
25
|
+
additionalProperties: {
|
|
26
|
+
effectiveDateTime: (0, measurement_time_1.recordedAtOrNow)({ recordedAt }).toISOString(),
|
|
27
|
+
component: components,
|
|
28
|
+
},
|
|
29
|
+
}];
|
|
30
|
+
}
|
|
31
|
+
getObservationCodes() {
|
|
32
|
+
return [this.codeableConcept()];
|
|
33
|
+
}
|
|
34
|
+
getAdditionalProperties(measurement) {
|
|
35
|
+
return [{ effectiveDateTime: (0, measurement_time_1.recordedAtOrNow)(this.getCastedMeasurement(measurement)).toISOString() }];
|
|
36
|
+
}
|
|
37
|
+
generateValues(_measurement) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
getUnits() {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
getMeasurementDisplay(measurement) {
|
|
44
|
+
return `${this.getCastedMeasurement(measurement).components.length} ${this.measurementTypeName} metrics`;
|
|
45
|
+
}
|
|
46
|
+
getObservationDisplay(observations) {
|
|
47
|
+
const count = observations.reduce((total, observation) => { var _a, _b; return total + ((_b = (_a = observation.component) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0); }, 0);
|
|
48
|
+
return count ? `${count} ${this.measurementTypeName} metrics` : undefined;
|
|
49
|
+
}
|
|
50
|
+
convertSamplesToMeasurement(_samples) {
|
|
51
|
+
throw new Error(`${this.title} samples are not supported.`);
|
|
52
|
+
}
|
|
53
|
+
convertRecordToMeasurement(_record) {
|
|
54
|
+
throw new Error(`${this.title} Health Connect records are not supported.`);
|
|
55
|
+
}
|
|
56
|
+
codeableConcept() {
|
|
57
|
+
return {
|
|
58
|
+
coding: [{
|
|
59
|
+
code: this.code,
|
|
60
|
+
display: `${this.title} observation`,
|
|
61
|
+
system: MEASUREMENT_TYPE_SYSTEM,
|
|
62
|
+
}],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const babySleepService = () => new ComponentObservationService(types_1.MeasurementTypeKey.babySleep, "baby sleep", types_1.ObservationCode.BABY_SLEEP);
|
|
67
|
+
exports.babySleepService = babySleepService;
|
|
68
|
+
const respiratoryTherapyService = () => new ComponentObservationService(types_1.MeasurementTypeKey.respiratoryTherapy, "respiratory therapy", types_1.ObservationCode.RESPIRATORY_THERAPY);
|
|
69
|
+
exports.respiratoryTherapyService = respiratoryTherapyService;
|
|
@@ -2,9 +2,8 @@ import { MeasurementTypeKey } from "../../../types";
|
|
|
2
2
|
import { IObservationService } from "../types/IObservationService";
|
|
3
3
|
export declare class ObservationServiceCreator {
|
|
4
4
|
private static instance;
|
|
5
|
-
private services;
|
|
5
|
+
private readonly services;
|
|
6
6
|
private constructor();
|
|
7
7
|
static getInstance(): ObservationServiceCreator;
|
|
8
|
-
private initializeServices;
|
|
9
8
|
getService(type: MeasurementTypeKey): IObservationService;
|
|
10
9
|
}
|