@ovok/core 0.4.34 → 0.4.36
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/observation/methods/generateObservationBodiesByMeasurement.js +19 -2
- package/dist/client/observation/types/generate-observation-body/GenerateObservationBodyParams.d.ts +2 -0
- package/dist/client/observation/types/measurement/MeasurementDevice.d.ts +9 -0
- package/dist/client/observation/utils/getDeviceObject.js +18 -4
- package/dist/client/offline/offline-measurement-queue.js +23 -4
- package/dist/conformance/capability-requirements.js +1 -1
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ exports.generateObservationBodiesByMeasurement = generateObservationBodiesByMeas
|
|
|
4
4
|
const core_1 = require("@medplum/core");
|
|
5
5
|
const creator_1 = require("../services/creator");
|
|
6
6
|
const getDeviceObject_1 = require("../utils/getDeviceObject");
|
|
7
|
+
const DEVICE_READING_IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/device-reading-id";
|
|
7
8
|
const getPatientReference = async (client, patientId) => {
|
|
8
9
|
if (patientId) {
|
|
9
10
|
return `Patient/${patientId}`;
|
|
@@ -11,6 +12,21 @@ const getPatientReference = async (client, patientId) => {
|
|
|
11
12
|
const profile = (await client.getProfileAsync());
|
|
12
13
|
return (0, core_1.getReferenceString)(profile);
|
|
13
14
|
};
|
|
15
|
+
const addSourceIdentifier = (observations, patientReference, sourceId) => patientReference && sourceId
|
|
16
|
+
? observations.map((observation, index) => {
|
|
17
|
+
var _a;
|
|
18
|
+
return ({
|
|
19
|
+
...observation,
|
|
20
|
+
identifier: [
|
|
21
|
+
...((_a = observation.identifier) !== null && _a !== void 0 ? _a : []),
|
|
22
|
+
{
|
|
23
|
+
system: DEVICE_READING_IDENTIFIER_SYSTEM,
|
|
24
|
+
value: `${patientReference}/${sourceId}/${index}`,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
})
|
|
29
|
+
: observations;
|
|
14
30
|
async function generateObservationBodiesByMeasurement(params) {
|
|
15
31
|
var _a;
|
|
16
32
|
const { device, measurement } = params;
|
|
@@ -26,7 +42,7 @@ async function generateObservationBodiesByMeasurement(params) {
|
|
|
26
42
|
}
|
|
27
43
|
: observation;
|
|
28
44
|
if (observationFragments) {
|
|
29
|
-
|
|
45
|
+
const observations = observationFragments
|
|
30
46
|
.map((fragment) => ({
|
|
31
47
|
resourceType: "Observation",
|
|
32
48
|
status: "registered",
|
|
@@ -40,6 +56,7 @@ async function generateObservationBodiesByMeasurement(params) {
|
|
|
40
56
|
}))
|
|
41
57
|
.filter(hasObservationValue)
|
|
42
58
|
.map(applyEffectiveDateTime);
|
|
59
|
+
return addSourceIdentifier(observations, patientReference, params.sourceId);
|
|
43
60
|
}
|
|
44
61
|
const observationCodes = observationService.getObservationCodes();
|
|
45
62
|
const values = observationService.generateValues(measurement);
|
|
@@ -55,7 +72,7 @@ async function generateObservationBodiesByMeasurement(params) {
|
|
|
55
72
|
code,
|
|
56
73
|
...(additionalProperties[index] || {}),
|
|
57
74
|
}));
|
|
58
|
-
return observationBodies.filter(hasObservationValue).map(applyEffectiveDateTime);
|
|
75
|
+
return addSourceIdentifier(observationBodies.filter(hasObservationValue).map(applyEffectiveDateTime), patientReference, params.sourceId);
|
|
59
76
|
}
|
|
60
77
|
const hasObservationValue = (observation) => {
|
|
61
78
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -16,6 +16,15 @@ export type MeasurementDevice = {
|
|
|
16
16
|
sn: string;
|
|
17
17
|
/** Runtime association key; not written to the FHIR device identifier. */
|
|
18
18
|
id: string;
|
|
19
|
+
/**
|
|
20
|
+
* The brand and model the advertised name identified, when one decoder serves several
|
|
21
|
+
* products (the mobile SDK's `deviceData.variant`). Preferred over `manufacturerName` and
|
|
22
|
+
* `model`, which often describe the Bluetooth module rather than the product.
|
|
23
|
+
*/
|
|
24
|
+
variant?: {
|
|
25
|
+
manufacturer: string;
|
|
26
|
+
model: string;
|
|
27
|
+
};
|
|
19
28
|
/**
|
|
20
29
|
* Remaining charge, 0 to 100.
|
|
21
30
|
*
|
|
@@ -8,6 +8,15 @@ const MANUFACTURER_NAME_URL = `${EXTENSION_BASE_URL}/device-manufacturer-name`;
|
|
|
8
8
|
const MODEL_URL = `${EXTENSION_BASE_URL}/device-model`;
|
|
9
9
|
const SERIAL_NUMBER_URL = `${EXTENSION_BASE_URL}/device-serial-number`;
|
|
10
10
|
const BATTERY_PERCENTAGE_URL = `${EXTENSION_BASE_URL}/device-battery-percentage`;
|
|
11
|
+
const PARSER_URL = `${EXTENSION_BASE_URL}/device-parser`;
|
|
12
|
+
/** The product's own name: its model, prefixed with the brand unless the model already says it. */
|
|
13
|
+
const variantDisplay = ({ manufacturer, model, }) => {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const brand = (_b = (_a = manufacturer.split(" ")[0]) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : "";
|
|
16
|
+
return brand && model.toLowerCase().startsWith(brand)
|
|
17
|
+
? model
|
|
18
|
+
: `${manufacturer} ${model}`;
|
|
19
|
+
};
|
|
11
20
|
/**
|
|
12
21
|
* The battery extension is omitted rather than defaulted when the device did not report
|
|
13
22
|
* one, because a placeholder charge level would read as a real reading.
|
|
@@ -30,7 +39,7 @@ const getOptionalStringExtension = (url, value) => {
|
|
|
30
39
|
return [{ url, valueString: value }];
|
|
31
40
|
};
|
|
32
41
|
const getDeviceObject = (measurementDevice) => {
|
|
33
|
-
var _a;
|
|
42
|
+
var _a, _b, _c, _d, _e;
|
|
34
43
|
if (!measurementDevice) {
|
|
35
44
|
return {
|
|
36
45
|
identifier: {
|
|
@@ -46,7 +55,9 @@ const getDeviceObject = (measurementDevice) => {
|
|
|
46
55
|
},
|
|
47
56
|
}
|
|
48
57
|
: {}),
|
|
49
|
-
display:
|
|
58
|
+
display: measurementDevice.variant
|
|
59
|
+
? variantDisplay(measurementDevice.variant)
|
|
60
|
+
: (_a = measurementDevice.name) !== null && _a !== void 0 ? _a : "no-name",
|
|
50
61
|
extension: [
|
|
51
62
|
{
|
|
52
63
|
url: LOCAL_NAME_URL,
|
|
@@ -56,8 +67,11 @@ const getDeviceObject = (measurementDevice) => {
|
|
|
56
67
|
url: MANUFACTURER_DATA_URL,
|
|
57
68
|
valueString: measurementDevice.manufacturerData || "no-manufacturer-data",
|
|
58
69
|
},
|
|
59
|
-
...getOptionalStringExtension(MANUFACTURER_NAME_URL, measurementDevice.manufacturerName),
|
|
60
|
-
...getOptionalStringExtension(MODEL_URL, measurementDevice.model),
|
|
70
|
+
...getOptionalStringExtension(MANUFACTURER_NAME_URL, (_c = (_b = measurementDevice.variant) === null || _b === void 0 ? void 0 : _b.manufacturer) !== null && _c !== void 0 ? _c : measurementDevice.manufacturerName),
|
|
71
|
+
...getOptionalStringExtension(MODEL_URL, (_e = (_d = measurementDevice.variant) === null || _d === void 0 ? void 0 : _d.model) !== null && _e !== void 0 ? _e : measurementDevice.model),
|
|
72
|
+
...(measurementDevice.variant
|
|
73
|
+
? getOptionalStringExtension(PARSER_URL, measurementDevice.name)
|
|
74
|
+
: []),
|
|
61
75
|
{
|
|
62
76
|
url: SERIAL_NUMBER_URL,
|
|
63
77
|
valueString: measurementDevice.sn || "no-serial-number",
|
|
@@ -8,6 +8,7 @@ const DEFAULT_RETRY_BACKOFF_MS = 5000;
|
|
|
8
8
|
const DEFAULT_MAX_RETRY_BACKOFF_MS = 60 * 60 * 1000;
|
|
9
9
|
const IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/offline-measurement-id";
|
|
10
10
|
exports.OFFLINE_MEASUREMENT_IDENTIFIER_SYSTEM = IDENTIFIER_SYSTEM;
|
|
11
|
+
const DEVICE_READING_IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/device-reading-id";
|
|
11
12
|
const canonicalize = (value) => {
|
|
12
13
|
if (value instanceof Date) {
|
|
13
14
|
return value.toISOString();
|
|
@@ -44,25 +45,43 @@ const stableValueId = (input) => {
|
|
|
44
45
|
hex[16] = ((Number.parseInt(hex[16], 16) & 0x3) | 0x8).toString(16);
|
|
45
46
|
return `${hex.slice(0, 8).join("")}-${hex.slice(8, 12).join("")}-${hex.slice(12, 16).join("")}-${hex.slice(16, 20).join("")}-${hex.slice(20).join("")}`;
|
|
46
47
|
};
|
|
47
|
-
const stableMeasurementId = (observation) =>
|
|
48
|
+
const stableMeasurementId = (observation) => {
|
|
49
|
+
const identifiers = observation.identifier;
|
|
50
|
+
if (!(identifiers === null || identifiers === void 0 ? void 0 : identifiers.some((identifier) => identifier.system === DEVICE_READING_IDENTIFIER_SYSTEM))) {
|
|
51
|
+
return stableValueId(observation);
|
|
52
|
+
}
|
|
53
|
+
const remainingIdentifiers = identifiers.filter((identifier) => identifier.system !== DEVICE_READING_IDENTIFIER_SYSTEM);
|
|
54
|
+
const legacyObservation = { ...observation };
|
|
55
|
+
if (remainingIdentifiers.length > 0) {
|
|
56
|
+
legacyObservation.identifier = remainingIdentifiers;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
delete legacyObservation.identifier;
|
|
60
|
+
}
|
|
61
|
+
return stableValueId(legacyObservation);
|
|
62
|
+
};
|
|
48
63
|
exports.stableMeasurementId = stableMeasurementId;
|
|
49
64
|
const entryForObservation = (observation) => {
|
|
50
|
-
var _a;
|
|
65
|
+
var _a, _b;
|
|
51
66
|
const id = stableMeasurementId(observation);
|
|
52
67
|
const identifier = {
|
|
53
68
|
system: IDENTIFIER_SYSTEM,
|
|
54
69
|
value: id,
|
|
55
70
|
};
|
|
71
|
+
const sourceIdentifier = (_a = observation.identifier) === null || _a === void 0 ? void 0 : _a.find((item) => item.system === DEVICE_READING_IDENTIFIER_SYSTEM);
|
|
72
|
+
const ifNoneExist = sourceIdentifier
|
|
73
|
+
? `identifier=${encodeURIComponent(`${IDENTIFIER_SYSTEM}|${id}`)},${encodeURIComponent(`${DEVICE_READING_IDENTIFIER_SYSTEM}|${sourceIdentifier.value}`)}`
|
|
74
|
+
: `identifier=${IDENTIFIER_SYSTEM}|${id}`;
|
|
56
75
|
return {
|
|
57
76
|
fullUrl: `urn:uuid:${id}`,
|
|
58
77
|
request: {
|
|
59
78
|
method: "POST",
|
|
60
79
|
url: "Observation",
|
|
61
|
-
ifNoneExist
|
|
80
|
+
ifNoneExist,
|
|
62
81
|
},
|
|
63
82
|
resource: {
|
|
64
83
|
...observation,
|
|
65
|
-
identifier: [...((
|
|
84
|
+
identifier: [...((_b = observation.identifier) !== null && _b !== void 0 ? _b : []), identifier],
|
|
66
85
|
},
|
|
67
86
|
};
|
|
68
87
|
};
|
|
@@ -48,7 +48,7 @@ exports.OVOK_CORE_REQUIREMENTS = {
|
|
|
48
48
|
description: "The FHIR surface a server must provide for the @ovok/core client library's own methods to function.",
|
|
49
49
|
software: {
|
|
50
50
|
name: "@ovok/core",
|
|
51
|
-
version: "0.4.
|
|
51
|
+
version: "0.4.36",
|
|
52
52
|
},
|
|
53
53
|
fhirVersion: "4.0.1",
|
|
54
54
|
format: ["json"],
|