nightscout
Version:
Nightscout acts as a web-based CGM (Continuous Glucose Monitor) to allow multiple caregivers to remotely view a patients glucose data in realtime.
301 lines (238 loc) • 8.14 kB
JavaScript
;
var _ = require('lodash');
var units = require('./units')();
var times = require('./times');
function init () {
var sbx = {};
function reset () {
sbx.properties = {};
}
function extend () {
sbx.unitsLabel = unitsLabel();
sbx.data = sbx.data || {};
//default to prevent adding checks everywhere
sbx.extendedSettings = { empty: true };
}
function withExtendedSettings (plugin, allExtendedSettings, sbx) {
var sbx2 = _.extend({}, sbx);
sbx2.extendedSettings = allExtendedSettings && allExtendedSettings[plugin.name] || {};
return sbx2;
}
/**
* A view into the safe notification functions for plugins
*
* @param ctx
* @returns {{notification}}
*/
function safeNotifications (ctx) {
return _.pick(ctx.notifications, ['requestNotify', 'requestSnooze', 'requestClear']);
}
/**
* Initialize the sandbox using server state
*
* @param env - .js
* @param ctx - created from bootevent
* @returns {{sbx}}
*/
sbx.serverInit = function serverInit (env, ctx) {
reset();
sbx.runtimeEnvironment = 'server';
sbx.runtimeState = ctx.runtimeState;
sbx.time = Date.now();
sbx.settings = env.settings;
sbx.data = ctx.ddata.clone();
sbx.notifications = safeNotifications(ctx);
sbx.levels = ctx.levels;
sbx.language = ctx.language;
sbx.translate = ctx.language.translate;
var profile = require('./profilefunctions')();
//Plugins will expect the right profile based on time
profile.loadData(_.cloneDeep(ctx.ddata.profiles));
profile.updateTreatments(ctx.ddata.profileTreatments, ctx.ddata.tempbasalTreatments, ctx.ddata.combobolusTreatments);
sbx.data.profile = profile;
delete sbx.data.profiles;
sbx.properties = {};
sbx.withExtendedSettings = function getPluginExtendedSettingsOnly (plugin) {
return withExtendedSettings(plugin, env.extendedSettings, sbx);
};
extend();
return sbx;
};
/**
* Initialize the sandbox using client state
*
* @param settings - specific settings from the client, starting with the defaults
* @param time - could be a retro time
* @param pluginBase - used by visualization plugins to update the UI
* @param data - svgs, treatments, profile, etc
* @returns {{sbx}}
*/
sbx.clientInit = function clientInit (ctx, time, data) {
reset();
sbx.runtimeEnvironment = 'client';
sbx.settings = ctx.settings;
sbx.showPlugins = ctx.settings.showPlugins;
sbx.time = time;
sbx.data = data;
sbx.pluginBase = ctx.pluginBase;
sbx.notifications = safeNotifications(ctx);
sbx.levels = ctx.levels;
sbx.language = ctx.language;
sbx.translate = ctx.language.translate;
if (sbx.pluginBase) {
sbx.pluginBase.forecastInfos = [];
sbx.pluginBase.forecastPoints = {};
}
sbx.extendedSettings = { empty: true };
sbx.withExtendedSettings = function getPluginExtendedSettingsOnly (plugin) {
return withExtendedSettings(plugin, sbx.settings.extendedSettings, sbx);
};
extend();
return sbx;
};
/**
* Properties are immutable, first plugin to set it wins, plugins should be in the correct order
*
* @param name
* @param setter
*/
sbx.offerProperty = function offerProperty (name, setter) {
if (!Object.keys(sbx.properties).includes(name)) {
var value = setter();
if (value) {
sbx.properties[name] = value;
}
}
};
sbx.isCurrent = function isCurrent (entry) {
return entry && sbx.time - entry.mills <= times.mins(15).msecs;
};
sbx.lastEntry = function lastEntry (entries) {
return _.findLast(entries, function notInTheFuture (entry) {
return sbx.entryMills(entry) <= sbx.time;
});
};
sbx.lastNEntries = function lastNEntries (entries, n) {
var lastN = [];
_.takeRightWhile(entries, function(entry) {
if (sbx.entryMills(entry) <= sbx.time) {
lastN.push(entry);
}
return lastN.length < n;
});
lastN.reverse();
return lastN;
};
sbx.prevEntry = function prevEntry (entries) {
var last2 = sbx.lastNEntries(entries, 2);
return _.first(last2);
};
sbx.prevSGVEntry = function prevSGVEntry () {
return sbx.prevEntry(sbx.data.sgvs);
};
sbx.lastSGVEntry = function lastSGVEntry () {
return sbx.lastEntry(sbx.data.sgvs);
};
sbx.lastSGVMgdl = function lastSGVMgdl () {
var last = sbx.lastSGVEntry();
return last && last.mgdl;
};
sbx.lastSGVMills = function lastSGVMills () {
return sbx.entryMills(sbx.lastSGVEntry());
};
sbx.entryMills = function entryMills (entry) {
return entry && entry.mills;
};
sbx.lastScaledSGV = function lastScaledSVG () {
return sbx.scaleEntry(sbx.lastSGVEntry());
};
sbx.lastDisplaySVG = function lastDisplaySVG () {
return sbx.displayBg(sbx.lastSGVEntry());
};
sbx.buildBGNowLine = function buildBGNowLine () {
var line = 'BG Now: ' + sbx.lastDisplaySVG();
var delta = sbx.properties.delta && sbx.properties.delta.display;
if (delta) {
line += ' ' + delta;
}
var direction = sbx.properties.direction && sbx.properties.direction.label;
if (direction) {
line += ' ' + direction;
}
line += ' ' + sbx.unitsLabel;
return line;
};
sbx.propertyLine = function propertyLine (propertyName) {
return sbx.properties[propertyName] && sbx.properties[propertyName].displayLine;
};
sbx.appendPropertyLine = function appendPropertyLine (propertyName, lines) {
lines = lines || [];
var displayLine = sbx.propertyLine(propertyName);
if (displayLine) {
lines.push(displayLine);
}
return lines;
};
sbx.prepareDefaultLines = function prepareDefaultLines () {
var lines = [sbx.buildBGNowLine()];
sbx.appendPropertyLine('rawbg', lines);
sbx.appendPropertyLine('ar2', lines);
sbx.appendPropertyLine('bwp', lines);
sbx.appendPropertyLine('iob', lines);
sbx.appendPropertyLine('cob', lines);
return lines;
};
sbx.buildDefaultMessage = function buildDefaultMessage () {
return sbx.prepareDefaultLines().join('\n');
};
sbx.displayBg = function displayBg (entry) {
var isDex = entry && (!entry.device || entry.device === 'dexcom' || entry.device === 'share2');
if (isDex && Number(entry.mgdl) === 39) {
return 'LOW';
} else if (isDex && Number(entry.mgdl) === 401) {
return 'HIGH';
} else {
return sbx.scaleEntry(entry);
}
};
sbx.scaleEntry = function scaleEntry (entry) {
if (entry && entry.scaled === undefined) {
if (sbx.settings.units === 'mmol') {
entry.scaled = entry.mmol || units.mgdlToMMOL(entry.mgdl);
} else {
entry.scaled = entry.mgdl || units.mmolToMgdl(entry.mmol);
}
}
return entry && Number(entry.scaled);
};
sbx.scaleMgdl = function scaleMgdl (mgdl) {
if (sbx.settings.units === 'mmol' && mgdl) {
return Number(units.mgdlToMMOL(mgdl));
} else {
return Number(mgdl);
}
};
sbx.roundInsulinForDisplayFormat = function roundInsulinForDisplayFormat (insulin) {
if (insulin === 0) {
return '0';
}
if (sbx.properties.roundingStyle === 'medtronic') {
var denominator = 0.1;
var digits = 1;
if (insulin <= 0.5) {
denominator = 0.05;
digits = 2;
}
return (Math.floor(insulin / denominator) * denominator).toFixed(digits);
}
return (Math.floor(insulin / 0.01) * 0.01).toFixed(2);
};
function unitsLabel () {
return sbx.settings.units === 'mmol' ? 'mmol/L' : 'mg/dl';
}
sbx.roundBGToDisplayFormat = function roundBGToDisplayFormat (bg) {
return sbx.settings.units === 'mmol' ? Math.round(bg * 10) / 10 : Math.round(bg);
};
return sbx;
}
module.exports = init;