UNPKG

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
'use strict'; 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;