mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
244 lines
7.2 KiB
JavaScript
244 lines
7.2 KiB
JavaScript
'use strict';
|
|
|
|
var _ = require('lodash');
|
|
var times = require('../times');
|
|
var consts = require('../constants');
|
|
|
|
var DEVICE_TYPE_FIELDS = ['uploader', 'pump', 'openaps', 'loop', 'xdripjs'];
|
|
|
|
function init () {
|
|
|
|
var ddata = {
|
|
sgvs: []
|
|
, treatments: []
|
|
, mbgs: []
|
|
, cals: []
|
|
, profiles: []
|
|
, devicestatus: []
|
|
, food: []
|
|
, activity: []
|
|
, lastUpdated: 0
|
|
};
|
|
|
|
ddata.clone = function clone () {
|
|
return _.clone(ddata, function(value) {
|
|
//special handling of mongo ObjectID's
|
|
//see https://github.com/lodash/lodash/issues/602#issuecomment-47414964
|
|
|
|
//instead of requiring Mongo.ObjectID here and having it get pulled into the bundle
|
|
//we'll look for the toHexString function and then assume it's an ObjectID
|
|
if (value && value.toHexString && value.toHexString.call && value.toString && value.toString.call) {
|
|
return value.toString();
|
|
}
|
|
});
|
|
};
|
|
|
|
ddata.dataWithRecentStatuses = function dataWithRecentStatuses() {
|
|
var results = {};
|
|
results.devicestatus = ddata.recentDeviceStatus(Date.now());
|
|
results.sgvs = ddata.sgvs;
|
|
results.cals = ddata.cals;
|
|
|
|
var profiles = _.cloneDeep(ddata.profiles);
|
|
if (profiles && profiles[0]) {
|
|
Object.keys(profiles[0].store).forEach(k => {
|
|
if (k.indexOf('@@@@@') > 0) {
|
|
delete profiles[0].store[k];
|
|
}
|
|
})
|
|
}
|
|
results.profiles = profiles;
|
|
results.mbgs = ddata.mbgs;
|
|
results.food = ddata.food;
|
|
results.treatments = ddata.treatments;
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
ddata.recentDeviceStatus = function recentDeviceStatus (time) {
|
|
|
|
var deviceAndTypes =
|
|
_.chain(ddata.devicestatus)
|
|
.map(function eachStatus (status) {
|
|
return _.chain(status)
|
|
.keys()
|
|
.filter(function isExcluded (key) {
|
|
return _.includes(DEVICE_TYPE_FIELDS, key);
|
|
})
|
|
.map(function toDeviceTypeKey (key) {
|
|
return {
|
|
device: status.device
|
|
, type: key
|
|
};
|
|
})
|
|
.value();
|
|
})
|
|
.flatten()
|
|
.uniqWith(_.isEqual)
|
|
.value();
|
|
|
|
//console.info('>>>deviceAndTypes', deviceAndTypes);
|
|
|
|
var rv = _.chain(deviceAndTypes)
|
|
.map(function findMostRecent (deviceAndType) {
|
|
return _.chain(ddata.devicestatus)
|
|
.filter(function isSameDeviceType (status) {
|
|
return status.device === deviceAndType.device && _.has(status, deviceAndType.type)
|
|
})
|
|
.filter(function notInTheFuture (status) {
|
|
return status.mills <= time;
|
|
})
|
|
.sortBy('mills')
|
|
.takeRight(10)
|
|
.value();
|
|
}).value();
|
|
|
|
var merged = [].concat.apply([], rv);
|
|
|
|
rv = _.chain(merged)
|
|
.filter(_.isObject)
|
|
.uniq('_id')
|
|
.sortBy('mills')
|
|
.value();
|
|
|
|
return rv;
|
|
|
|
};
|
|
|
|
ddata.processDurations = function processDurations (treatments, keepzeroduration) {
|
|
|
|
treatments = _.uniqBy(treatments, 'mills');
|
|
|
|
// cut temp basals by end events
|
|
// better to do it only on data update
|
|
var endevents = treatments.filter(function filterEnd (t) {
|
|
return !t.duration;
|
|
});
|
|
|
|
function cutIfInInterval (base, end) {
|
|
if (base.mills < end.mills && base.mills + times.mins(base.duration).msecs > end.mills) {
|
|
base.duration = times.msecs(end.mills - base.mills).mins;
|
|
if (end.profile) {
|
|
base.cuttedby = end.profile;
|
|
end.cutting = base.profile;
|
|
}
|
|
}
|
|
}
|
|
|
|
// cut by end events
|
|
treatments.forEach(function allTreatments (t) {
|
|
if (t.duration) {
|
|
endevents.forEach(function allEndevents (e) {
|
|
cutIfInInterval(t, e);
|
|
});
|
|
}
|
|
});
|
|
|
|
// cut by overlaping events
|
|
treatments.forEach(function allTreatments (t) {
|
|
if (t.duration) {
|
|
treatments.forEach(function allEndevents (e) {
|
|
cutIfInInterval(t, e);
|
|
});
|
|
}
|
|
});
|
|
|
|
if (keepzeroduration) {
|
|
return treatments;
|
|
} else {
|
|
return treatments.filter(function filterEnd (t) {
|
|
return t.duration;
|
|
});
|
|
}
|
|
};
|
|
|
|
ddata.processTreatments = function processTreatments (preserveOrignalTreatments) {
|
|
|
|
// filter & prepare 'Site Change' events
|
|
ddata.sitechangeTreatments = ddata.treatments.filter(function filterSensor (t) {
|
|
return t.eventType.indexOf('Site Change') > -1;
|
|
}).sort(function(a, b) {
|
|
return a.mills > b.mills;
|
|
});
|
|
|
|
// filter & prepare 'Insulin Change' events
|
|
ddata.insulinchangeTreatments = ddata.treatments.filter(function filterInsulin (t) {
|
|
return t.eventType.indexOf('Insulin Change') > -1;
|
|
}).sort(function(a, b) {
|
|
return a.mills > b.mills;
|
|
});
|
|
|
|
// filter & prepare 'Pump Battery Change' events
|
|
ddata.batteryTreatments = ddata.treatments.filter(function filterSensor (t) {
|
|
return t.eventType.indexOf('Pump Battery Change') > -1;
|
|
}).sort(function(a, b) {
|
|
return a.mills > b.mills;
|
|
});
|
|
|
|
// filter & prepare 'Sensor' events
|
|
ddata.sensorTreatments = ddata.treatments.filter(function filterSensor (t) {
|
|
return t.eventType.indexOf('Sensor') > -1;
|
|
}).sort(function(a, b) {
|
|
return a.mills > b.mills;
|
|
});
|
|
|
|
// filter & prepare 'Profile Switch' events
|
|
var profileTreatments = ddata.treatments.filter(function filterProfiles (t) {
|
|
return t.eventType === 'Profile Switch';
|
|
}).sort(function(a, b) {
|
|
return a.mills > b.mills;
|
|
});
|
|
if (preserveOrignalTreatments)
|
|
profileTreatments = _.cloneDeep(profileTreatments);
|
|
ddata.profileTreatments = ddata.processDurations(profileTreatments, true);
|
|
|
|
// filter & prepare 'Combo Bolus' events
|
|
ddata.combobolusTreatments = ddata.treatments.filter(function filterComboBoluses (t) {
|
|
return t.eventType === 'Combo Bolus';
|
|
}).sort(function(a, b) {
|
|
return a.mills > b.mills;
|
|
});
|
|
|
|
// filter & prepare temp basals
|
|
var tempbasalTreatments = ddata.treatments.filter(function filterBasals (t) {
|
|
return t.eventType && t.eventType.indexOf('Temp Basal') > -1;
|
|
});
|
|
if (preserveOrignalTreatments)
|
|
tempbasalTreatments = _.cloneDeep(tempbasalTreatments);
|
|
ddata.tempbasalTreatments = ddata.processDurations(tempbasalTreatments, false);
|
|
|
|
// filter temp target
|
|
var tempTargetTreatments = ddata.treatments.filter(function filterTargets (t) {
|
|
//check for a units being sent
|
|
if (t.units) {
|
|
if (t.units == 'mmol') {
|
|
//convert to mgdl
|
|
t.targetTop = t.targetTop * consts.MMOL_TO_MGDL;
|
|
t.targetBottom = t.targetBottom * consts.MMOL_TO_MGDL;
|
|
t.units = 'mg/dl';
|
|
}
|
|
}
|
|
//if we have a temp target thats below 20, assume its mmol and convert to mgdl for safety.
|
|
if (t.targetTop < 20) {
|
|
t.targetTop = t.targetTop * consts.MMOL_TO_MGDL;
|
|
t.units = 'mg/dl';
|
|
}
|
|
if (t.targetBottom < 20) {
|
|
t.targetBottom = t.targetBottom * consts.MMOL_TO_MGDL;
|
|
t.units = 'mg/dl';
|
|
}
|
|
return t.eventType && t.eventType.indexOf('Temporary Target') > -1;
|
|
});
|
|
if (preserveOrignalTreatments)
|
|
tempTargetTreatments = _.cloneDeep(tempTargetTreatments);
|
|
ddata.tempTargetTreatments = ddata.processDurations(tempTargetTreatments, false);
|
|
|
|
};
|
|
|
|
return ddata;
|
|
|
|
}
|
|
|
|
module.exports = init;
|