From ea1dbccdb8f8b84c0f35603d818d07219a74e885 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Thu, 25 Sep 2014 00:57:33 -0700 Subject: [PATCH 01/22] forgot to git add some profile api files --- lib/api/profile/index.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++ lib/profile.js | 24 ++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 lib/api/profile/index.js create mode 100644 lib/profile.js diff --git a/lib/api/profile/index.js b/lib/api/profile/index.js new file mode 100644 index 000000000..926bbdfdc --- /dev/null +++ b/lib/api/profile/index.js @@ -0,0 +1,47 @@ +'use strict'; + +var consts = require('../../constants'); + +function configure (app, wares, profile) { + var express = require('express'), + api = express.Router( ); + + // invoke common middleware + api.use(wares.sendJSONStatus); + // text body types get handled as raw buffer stream + api.use(wares.bodyParser.raw( )); + // json body types get handled as parsed json + api.use(wares.bodyParser.json( )); + // also support url-encoded content-type + api.use(wares.bodyParser.urlencoded({ extended: true })); + + // List settings available + api.get('/profile/', function(req, res) { + profile.list(function (err, attribute) { + return res.json(attribute); + }); + }); + + function config_authed (app, api, wares, profile) { + + api.post('/profile/', /*TODO: auth disabled for now, need to get login figured out... wares.verifyAuthorization, */ function(req, res) { + var attribute = req.body; + profile.create(attribute, function (err, created) { + if (err) + res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + else + res.json(created); + }); + }); + + } + + if (app.enabled('api')) { + config_authed(app, api, wares, profile); + } + + return api; +} + +module.exports = configure; + diff --git a/lib/profile.js b/lib/profile.js new file mode 100644 index 000000000..c7ba0e101 --- /dev/null +++ b/lib/profile.js @@ -0,0 +1,24 @@ +'use strict'; + +function configure (collection, storage) { + + function create (obj, fn) { + obj.created_at = (new Date( )).toISOString( ); + api( ).insert(obj, function (err, doc) { + fn(null, doc); + }); + } + + function list (fn) { + return api( ).find({ }).sort({created_at: -1}).toArray(fn); + } + + function api ( ) { + return storage.pool.db.collection(collection); + } + + api.list = list; + api.create = create; + return api; +} +module.exports = configure; From 7653ef5b205f27ea9a7f85f901f0cc3958758a86 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Thu, 25 Sep 2014 01:09:55 -0700 Subject: [PATCH 02/22] no POSTs yet --- lib/api/profile/index.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/api/profile/index.js b/lib/api/profile/index.js index 926bbdfdc..5dae6a971 100644 --- a/lib/api/profile/index.js +++ b/lib/api/profile/index.js @@ -22,24 +22,6 @@ function configure (app, wares, profile) { }); }); - function config_authed (app, api, wares, profile) { - - api.post('/profile/', /*TODO: auth disabled for now, need to get login figured out... wares.verifyAuthorization, */ function(req, res) { - var attribute = req.body; - profile.create(attribute, function (err, created) { - if (err) - res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); - else - res.json(created); - }); - }); - - } - - if (app.enabled('api')) { - config_authed(app, api, wares, profile); - } - return api; } From 6080f6781683c33faffe65efc274d1ee869970f0 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Thu, 25 Sep 2014 00:55:13 -0700 Subject: [PATCH 03/22] more cherry-picking and porting of the profile api/storage from iob-cob --- env.js | 1 + lib/api/index.js | 3 ++- lib/websocket.js | 25 +++++++++++++++++++++---- server.js | 5 +++-- static/js/client.js | 7 ++++--- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/env.js b/env.js index 0218d6963..c063c2274 100644 --- a/env.js +++ b/env.js @@ -41,6 +41,7 @@ function config ( ) { env.mongo_collection = readENV('MONGO_COLLECTION', 'entries'); env.settings_collection = readENV('MONGO_SETTINGS_COLLECTION', 'settings'); env.treatments_collection = readENV('MONGO_TREATMENTS_COLLECTION', 'treatments'); + env.profile_collection = readENV('MONGO_PROFILE_COLLECTION', 'profile'); env.devicestatus_collection = readENV('MONGO_DEVICESTATUS_COLLECTION', 'devicestatus'); env.enable = readENV('ENABLE'); diff --git a/lib/api/index.js b/lib/api/index.js index 13ccb4c57..994306139 100644 --- a/lib/api/index.js +++ b/lib/api/index.js @@ -1,6 +1,6 @@ 'use strict'; -function create (env, entries, settings, treatments, devicestatus) { +function create (env, entries, settings, treatments, profile, devicestatus) { var express = require('express'), app = express( ) ; @@ -47,6 +47,7 @@ function create (env, entries, settings, treatments, devicestatus) { app.use('/', require('./entries/')(app, wares, entries)); app.use('/', require('./settings/')(app, wares, settings)); app.use('/', require('./treatments/')(app, wares, treatments)); + app.use('/', require('./profile/')(app, wares, profile)); app.use('/', require('./devicestatus/')(app, wares, devicestatus)); // Status diff --git a/lib/websocket.js b/lib/websocket.js index 49d10bedb..6936ef173 100644 --- a/lib/websocket.js +++ b/lib/websocket.js @@ -1,5 +1,5 @@ -function websocket (env, server, entries, treatments) { +function websocket (env, server, entries, treatments, profiles) { "use strict"; // CONSTANTS var ONE_HOUR = 3600000, @@ -29,6 +29,7 @@ var dir2Char = { treatmentData = [], mbgData = [], calData = [], + profileData = [], patientData = []; function start ( ) { @@ -146,6 +147,7 @@ function update() { cgmData = []; treatmentData = []; mbgData = []; + profileData = []; var earliest_data = now - TWO_DAYS; var q = { find: {"date": {"$gte": earliest_data}} }; entries.list(q, function (err, results) { @@ -188,8 +190,19 @@ function update() { treatment.x = timestamp.getTime(); return treatment; }); - // all done, do loadData - loadData( ); + + profiles.list(function (err, results) { + // There should be only one document in the profile collection with a DIA. If there are multiple, use the last one. + results.forEach(function(element, index, array) { + if (element) { + if (element.dia) { + profileData[0] = element; + } + } + }); + // all done, do loadData + loadData( ); + }); }); }); @@ -237,6 +250,10 @@ function loadData() { }); } + if (profileData) { + var profile = profileData; + } + if (actualCurrent && actualCurrent < 39) errorCode = actualCurrent; var actualLength = actual.length - 1; @@ -271,7 +288,7 @@ function loadData() { // consolidate and send the data to the client var shouldEmit = is_different(actual, predicted, mbg, treatment, cal); - patientData = [actual, predicted, mbg, treatment, cal]; + patientData = [actual, predicted, mbg, treatment, profile, cal]; console.log('patientData', patientData.length); if (shouldEmit) { emitData( ); diff --git a/server.js b/server.js index b1a5dd8b2..7c761c142 100644 --- a/server.js +++ b/server.js @@ -50,8 +50,9 @@ var express = require('express'); var entriesStorage = entries.storage(env.mongo_collection, store, pushover); var settings = require('./lib/settings')(env.settings_collection, store); var treatmentsStorage = treatments.storage(env.treatments_collection, store, pushover); +var profile = require('./lib/profile')(env.profile_collection, store); var devicestatusStorage = devicestatus.storage(env.devicestatus_collection, store); -var api = require('./lib/api/')(env, entriesStorage, settings, treatmentsStorage, devicestatusStorage); +var api = require('./lib/api/')(env, entriesStorage, settings, treatmentsStorage, profile, devicestatusStorage); var pebble = require('./lib/pebble'); /////////////////////////////////////////////////// @@ -105,7 +106,7 @@ store(function ready ( ) { // setup socket io for data and message transmission /////////////////////////////////////////////////// var websocket = require('./lib/websocket'); - var io = websocket(env, server, entriesStorage, treatmentsStorage); + var io = websocket(env, server, entriesStorage, treatmentsStorage, profile); }); /////////////////////////////////////////////////// diff --git a/static/js/client.js b/static/js/client.js index 3fd39c675..c073f00ee 100644 --- a/static/js/client.js +++ b/static/js/client.js @@ -28,6 +28,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; , latestUpdateTime , prevSGV , treatments + , profile , cal , padding = { top: 20, right: 10, bottom: 30, left: 10 } , opacity = {current: 1, DAY: 1, NIGHT: 0.5} @@ -124,7 +125,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; } else if (noise < 2 && browserSettings.showRawbg != 'always') { return 0; } else if (filtered == 0 || sgv < 40) { - console.info("Skipping ratio adjustment for SGV " + sgv); + console.info('Skipping ratio adjustment for SGV ' + sgv); return scale * (unfiltered - intercept) / slope; } else { var ratio = scale * (filtered - intercept) / slope / sgv; @@ -1335,8 +1336,8 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; d.created_at = new Date(d.created_at); }); - cal = d[4][d[4].length-1]; - + profile = d[4][0]; + cal = d[5][d[5].length-1]; var temp1 = [ ]; if (cal && showRawBGs()) { From 7f550b8cf5ec4163dc094fff9677717c0763a30e Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 10:56:40 -0800 Subject: [PATCH 04/22] start getting browserify-express hooked up to expose a shared IOB module --- .gitignore | 3 ++ bundle/bundle.source.js | 12 ++++++++ lib/iob.js | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- server.js | 13 ++++++++ static/index.html | 1 + 6 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 bundle/bundle.source.js create mode 100644 lib/iob.js diff --git a/.gitignore b/.gitignore index b8b6cad51..06a841420 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ bower_components/ node_modules/ + +bundle/bundle.out.js + .idea/ *.iml my.env diff --git a/bundle/bundle.source.js b/bundle/bundle.source.js new file mode 100644 index 000000000..e9c8f6e8d --- /dev/null +++ b/bundle/bundle.source.js @@ -0,0 +1,12 @@ +(function () { + + window.Nightscout = window.Nightscout || {}; + + window.Nightscout = { + iob: require('../lib/iob')() + }; + + console.info("Nightscout bundle ready", window.Nightscout); + +})(); + diff --git a/lib/iob.js b/lib/iob.js new file mode 100644 index 000000000..c8f48afaf --- /dev/null +++ b/lib/iob.js @@ -0,0 +1,81 @@ +'use strict'; + +function iobTotal(treatments, profile, time) { + var iob= 0; + var activity = 0; + if (!treatments) return {}; + if (typeof time === 'undefined') { + time = new Date(); + } + + treatments.forEach(function(treatment) { + if(treatment.created_at < time) { + var tIOB = iobCalc(treatment, profile, time); + if (tIOB && tIOB.iobContrib) iob += tIOB.iobContrib; + if (tIOB && tIOB.activityContrib) activity += tIOB.activityContrib; + } + }); + return { + iob: iob, + activity: activity + }; +} + +function iobCalc(treatment, profile, time) { + + var dia=profile.dia; + var scaleFactor = 3.0/dia; + var peak = 75; + var sens=profile.sens; + var iobContrib, activityContrib; + var t = time; + if (typeof t === 'undefined') { + t = new Date(); + } + + + if (treatment.insulin) { + var bolusTime=new Date(treatment.created_at); + var minAgo=scaleFactor*(t-bolusTime)/1000/60; + + if (minAgo < 0) { + iobContrib=0; + activityContrib=0; + } + if (minAgo < peak) { + var x = minAgo/5+1; + iobContrib=treatment.insulin*(1-0.001852*x*x+0.001852*x); + activityContrib=sens*treatment.insulin*(2/dia/60/peak)*minAgo; + + } + else if (minAgo < 180) { + var x = (minAgo-75)/5; + iobContrib=treatment.insulin*(0.001323*x*x - .054233*x + .55556); + activityContrib=sens*treatment.insulin*(2/dia/60-(minAgo-peak)*2/dia/60/(60*dia-peak)); + } + else { + iobContrib=0; + activityContrib=0; + } + return { + iobContrib: iobContrib, + activityContrib: activityContrib + }; + } else { + return ''; + } +} + +function IOB(opts) { + + var IOB = { + total: iobTotal + }; + + return IOB; + +} + +if (window) window.IOB = IOB; + +module.exports = IOB; \ No newline at end of file diff --git a/package.json b/package.json index af40c524b..7521c1ae0 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "pushover-notifications": "0.2.0", "sgvdata": "0.0.2", "socket.io": "^0.9.17", - "git-rev": "git://github.com/bewest/git-rev.git" + "git-rev": "git://github.com/bewest/git-rev.git", + "browserify-express": "^0.1.4" }, "devDependencies": { "istanbul": "~0.3.5", diff --git a/server.js b/server.js index b1a5dd8b2..d71380199 100644 --- a/server.js +++ b/server.js @@ -82,6 +82,19 @@ var staticFiles = express.static(env.static_files, {maxAge: 60 * 60 * 1000}); // serve the static content app.use(staticFiles); +var browserify_express = require('browserify-express'); +var bundle = browserify_express({ + entry: __dirname + '/bundle/bundle.source.js', + watch: __dirname + '/lib/', + mount: '/public/js/bundle.js', + verbose: true, + //minify: true, + bundle_opts: { debug: true }, // enable inline sourcemap on js files + write_file: __dirname + '/bundle/bundle.out.js' +}); + +app.use(bundle); + // Handle errors with express's errorhandler, to display more readable error messages. var errorhandler = require('errorhandler'); //if (process.env.NODE_ENV === 'development') { diff --git a/static/index.html b/static/index.html index d121858dc..cee26186d 100644 --- a/static/index.html +++ b/static/index.html @@ -220,6 +220,7 @@

Nightscout

+ From ba05243dddd53119ca0906b8be95e96be14b79c1 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 11:24:35 -0800 Subject: [PATCH 05/22] use IOB module to display IOB on th chart --- lib/iob.js | 3 ++- static/js/client.js | 32 ++++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/iob.js b/lib/iob.js index c8f48afaf..1de96b353 100644 --- a/lib/iob.js +++ b/lib/iob.js @@ -17,6 +17,7 @@ function iobTotal(treatments, profile, time) { }); return { iob: iob, + display: Math.round(iob * 10) / 10, activity: activity }; } @@ -69,7 +70,7 @@ function iobCalc(treatment, profile, time) { function IOB(opts) { var IOB = { - total: iobTotal + calcTotal: iobTotal }; return IOB; diff --git a/static/js/client.js b/static/js/client.js index c073f00ee..85891eeee 100644 --- a/static/js/client.js +++ b/static/js/client.js @@ -106,10 +106,17 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; function showRawBGs() { return app.enabledOptions - && app.enabledOptions.indexOf('rawbg' > -1) + && app.enabledOptions.indexOf('rawbg') > -1 && (browserSettings.showRawbg == 'always' || browserSettings.showRawbg == 'noise'); } + function showIOB() { + console.info("app.enabledOptions", app.enabledOptions); + console.info("app.enabledOptions.indexOf('iob')", app.enabledOptions.indexOf('iob' > -1)); + return app.enabledOptions + && app.enabledOptions.indexOf('iob') > -1; + } + function rawIsigToRawBg(entry, cal) { var unfiltered = parseInt(entry.unfiltered) || 0 @@ -358,6 +365,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; }); if (inRetroMode()) { + var time = new Date(brushExtent[1] - THIRTY_MINS_IN_MS); // filter data for -12 and +5 minutes from reference time for retrospective focus data prediction var lookbackTime = (lookback + 2) * FIVE_MINS_IN_MS + 2 * ONE_MIN_IN_MS; nowData = nowData.filter(function(d) { @@ -379,9 +387,17 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; updateCurrentSGV(focusPoint.y); + var details = calcBGDelta(prevfocusPoint.y, focusPoint.y); + + if (showIOB()) { + var iob = Nightscout.iob.calcTotal(treatments, profile, time); + console.info("retro IOB", iob); + details += ", IOB:" + iob.display; + } + currentBG.css('text-decoration','line-through'); currentDirection.html(focusPoint.y < 39 ? '✖' : focusPoint.direction); - currentDetails.text(calcBGDelta(prevfocusPoint.y, focusPoint.y)).css('text-decoration','line-through'); + currentDetails.text(details).css('text-decoration','line-through'); } else { currentBG.text('---').css('text-decoration',''); currentDirection.text('-'); @@ -389,7 +405,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; } $('#currentTime') - .text(formatTime(new Date(brushExtent[1] - THIRTY_MINS_IN_MS))) + .text(formatTime(time)) .css('text-decoration','line-through'); $('#lastEntry').text('RETRO').removeClass('current'); @@ -402,9 +418,17 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; updateClockDisplay(); updateTimeAgo(); + var details = calcBGDelta(prevSGV.y, latestSGV.y); + + if (showIOB()) { + var iob = Nightscout.iob.calcTotal(treatments, profile, nowDate); + console.info("current IOB", iob); + details += ", IOB:" + iob.display; + } + currentBG.css('text-decoration', ''); currentDirection.html(latestSGV.y < 39 ? '✖' : latestSGV.direction); - currentDetails.text(calcBGDelta(prevSGV.y, latestSGV.y)).css('text-decoration',''); + currentDetails.text(details).css('text-decoration',''); } xScale.domain(brush.extent()); From d9f0c4cd10e6e5bb033c4f43290277a746111583 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 11:31:37 -0800 Subject: [PATCH 06/22] removed debugs --- static/js/client.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/static/js/client.js b/static/js/client.js index 85891eeee..9962173e2 100644 --- a/static/js/client.js +++ b/static/js/client.js @@ -111,8 +111,6 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; } function showIOB() { - console.info("app.enabledOptions", app.enabledOptions); - console.info("app.enabledOptions.indexOf('iob')", app.enabledOptions.indexOf('iob' > -1)); return app.enabledOptions && app.enabledOptions.indexOf('iob') > -1; } @@ -391,7 +389,6 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; if (showIOB()) { var iob = Nightscout.iob.calcTotal(treatments, profile, time); - console.info("retro IOB", iob); details += ", IOB:" + iob.display; } @@ -422,7 +419,6 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; if (showIOB()) { var iob = Nightscout.iob.calcTotal(treatments, profile, nowDate); - console.info("current IOB", iob); details += ", IOB:" + iob.display; } From a693304ab2eaac861589d4072b971b20268e3197 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 11:48:55 -0800 Subject: [PATCH 07/22] now using new IOB module for /pebble --- lib/iob.js | 9 ++++++--- lib/pebble.js | 25 +++++++++++++++++++++++-- server.js | 4 ++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/iob.js b/lib/iob.js index 1de96b353..38f7674bf 100644 --- a/lib/iob.js +++ b/lib/iob.js @@ -1,6 +1,11 @@ 'use strict'; function iobTotal(treatments, profile, time) { + + console.info(">>>>treatments:", treatments); + console.info(">>>>profile:", profile); + console.info(">>>>time:", time); + var iob= 0; var activity = 0; if (!treatments) return {}; @@ -9,7 +14,7 @@ function iobTotal(treatments, profile, time) { } treatments.forEach(function(treatment) { - if(treatment.created_at < time) { + if(new Date(treatment.created_at) < time) { var tIOB = iobCalc(treatment, profile, time); if (tIOB && tIOB.iobContrib) iob += tIOB.iobContrib; if (tIOB && tIOB.activityContrib) activity += tIOB.activityContrib; @@ -77,6 +82,4 @@ function IOB(opts) { } -if (window) window.IOB = IOB; - module.exports = IOB; \ No newline at end of file diff --git a/lib/pebble.js b/lib/pebble.js index d8ea2f716..427ee2299 100644 --- a/lib/pebble.js +++ b/lib/pebble.js @@ -11,6 +11,8 @@ var DIRECTIONS = { , 'RATE OUT OF RANGE': 9 }; +var iob = require("./iob")(); + function directionToTrend (direction) { var trend = 8; if (direction in DIRECTIONS) { @@ -22,6 +24,8 @@ function directionToTrend (direction) { function pebble (req, res) { var ONE_DAY = 24 * 60 * 60 * 1000; var uploaderBattery; + var treatmentResults; + var profileResult; function requestMetric() { var units = req.query.units; @@ -84,6 +88,7 @@ function pebble (req, res) { var bgs = sgvData.slice(0, count); //for compatibility we're keeping battery here, but it would be better somewhere else bgs[0].battery = uploaderBattery ? "" + uploaderBattery : undefined; + bgs[0].iob = iob.calcTotal(treatmentResults.slice(0, 20), profileResult, new Date(now)).display; var result = { status: [ {now:now}], bgs: bgs, cals: calData.slice(0, count) }; res.setHeader('content-type', 'application/json'); @@ -100,12 +105,28 @@ function pebble (req, res) { var earliest_data = Date.now() - ONE_DAY; var q = { find: {"date": {"$gte": earliest_data}} }; - req.entries.list(q, get_latest); + var tq = { find: {"created_at": {"$gte": new Date(earliest_data).toISOString()}} }; + req.treatments.list(tq, function (err, trs) { + treatmentResults = trs; + req.profile.list(function (err, profileResults) { + profileResults.forEach(function(profile) { + if (profile) { + if (profile.dia) { + profileResult = profile; + } + } + }); + req.entries.list(q, get_latest); + }); + }); }); } -function configure (entries, devicestatus, env) { + +function configure (entries, treatments, profile, devicestatus, env) { function middle (req, res, next) { req.entries = entries; + req.treatments = treatments; + req.profile = profile; req.devicestatus = devicestatus; req.rawbg = env.enable && env.enable.indexOf('rawbg') > -1; next( ); diff --git a/server.js b/server.js index 8ea35abe1..be6cf5222 100644 --- a/server.js +++ b/server.js @@ -72,7 +72,7 @@ app.enable('trust proxy'); // Allows req.secure test on heroku https connections app.use('/api/v1', api); // pebble data -app.get('/pebble', pebble(entriesStorage, devicestatusStorage, env)); +app.get('/pebble', pebble(entriesStorage, treatmentsStorage, profile, devicestatusStorage, env)); //app.get('/package.json', software); @@ -86,7 +86,7 @@ app.use(staticFiles); var browserify_express = require('browserify-express'); var bundle = browserify_express({ entry: __dirname + '/bundle/bundle.source.js', - watch: __dirname + '/lib/', + watch: [__dirname + '/lib/', __dirname + '/bundle/bundle.source.js'], mount: '/public/js/bundle.js', verbose: true, //minify: true, From 85a19c765fc282ba4a132ea9fe7bb8914f1c4bcf Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 11:50:16 -0800 Subject: [PATCH 08/22] clean up --- lib/iob.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/iob.js b/lib/iob.js index 38f7674bf..2c8a38eb1 100644 --- a/lib/iob.js +++ b/lib/iob.js @@ -2,10 +2,6 @@ function iobTotal(treatments, profile, time) { - console.info(">>>>treatments:", treatments); - console.info(">>>>profile:", profile); - console.info(">>>>time:", time); - var iob= 0; var activity = 0; if (!treatments) return {}; @@ -22,7 +18,7 @@ function iobTotal(treatments, profile, time) { }); return { iob: iob, - display: Math.round(iob * 10) / 10, + display: Math.round(iob * 100) / 100, activity: activity }; } From 5a13393d552a085077213ce9b9e25fa5de11f99b Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 12:02:41 -0800 Subject: [PATCH 09/22] only load treatments and profile if iob is enabled --- lib/pebble.js | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/lib/pebble.js b/lib/pebble.js index 427ee2299..22d99d6f7 100644 --- a/lib/pebble.js +++ b/lib/pebble.js @@ -86,9 +86,11 @@ function pebble (req, res) { var count = parseInt(req.query.count) || 1; var bgs = sgvData.slice(0, count); - //for compatibility we're keeping battery here, but it would be better somewhere else + //for compatibility we're keeping battery and iob here, but they would be better somewhere else bgs[0].battery = uploaderBattery ? "" + uploaderBattery : undefined; - bgs[0].iob = iob.calcTotal(treatmentResults.slice(0, 20), profileResult, new Date(now)).display; + if (req.iob) { + bgs[0].iob = iob.calcTotal(treatmentResults.slice(0, 20), profileResult, new Date(now)).display; + } var result = { status: [ {now:now}], bgs: bgs, cals: calData.slice(0, count) }; res.setHeader('content-type', 'application/json'); @@ -104,24 +106,40 @@ function pebble (req, res) { } var earliest_data = Date.now() - ONE_DAY; - var q = { find: {"date": {"$gte": earliest_data}} }; - var tq = { find: {"created_at": {"$gte": new Date(earliest_data).toISOString()}} }; - req.treatments.list(tq, function (err, trs) { + loadTreatments(req, earliest_data, function (err, trs) { treatmentResults = trs; - req.profile.list(function (err, profileResults) { - profileResults.forEach(function(profile) { - if (profile) { - if (profile.dia) { - profileResult = profile; - } - } - }); - req.entries.list(q, get_latest); + loadProfile(req, function (err, profileResults) { + profileResults.forEach(function(profile) { + if (profile) { + if (profile.dia) { + profileResult = profile; + } + } + }); + var q = { find: {"date": {"$gte": earliest_data}} }; + req.entries.list(q, get_latest); }); }); }); } +function loadTreatments(req, earliest_data, fn) { + if (req.iob) { + var q = { find: {"created_at": {"$gte": new Date(earliest_data).toISOString()}} }; + req.treatments.list(q, fn); + } else { + fn(null, []); + } +} + +function loadProfile(req, fn) { + if (req.iob) { + req.profile.list(fn); + } else { + fn(null, []); + } +} + function configure (entries, treatments, profile, devicestatus, env) { function middle (req, res, next) { req.entries = entries; @@ -129,6 +147,7 @@ function configure (entries, treatments, profile, devicestatus, env) { req.profile = profile; req.devicestatus = devicestatus; req.rawbg = env.enable && env.enable.indexOf('rawbg') > -1; + req.iob = env.enable && env.enable.indexOf('iob') > -1; next( ); } return [middle, pebble]; From ee9dcae87e127ac170bdcce1ed2fb92fc641ce3e Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 12:11:43 -0800 Subject: [PATCH 10/22] more clean up --- lib/iob.js | 119 +++++++++++++++++++++++++--------------------------- static/js/client.js | 4 +- 2 files changed, 60 insertions(+), 63 deletions(-) diff --git a/lib/iob.js b/lib/iob.js index 2c8a38eb1..b9629b2f9 100644 --- a/lib/iob.js +++ b/lib/iob.js @@ -1,80 +1,77 @@ 'use strict'; -function iobTotal(treatments, profile, time) { +function calcTotal(treatments, profile, time) { - var iob= 0; - var activity = 0; - if (!treatments) return {}; - if (typeof time === 'undefined') { - time = new Date(); + var iob = 0 + , activity = 0; + + if (!treatments) return {}; + + if (typeof profile === 'undefined') { + //if there is no profile default to 3 hour dia + profile = {dia: 3, sens: 0}; + } + + if (typeof time === 'undefined') { + time = new Date(); + } + + treatments.forEach(function (treatment) { + if (new Date(treatment.created_at) < time) { + var tIOB = calcTreatment(treatment, profile, time); + if (tIOB && tIOB.iobContrib) iob += tIOB.iobContrib; + if (tIOB && tIOB.activityContrib) activity += tIOB.activityContrib; } + }); - treatments.forEach(function(treatment) { - if(new Date(treatment.created_at) < time) { - var tIOB = iobCalc(treatment, profile, time); - if (tIOB && tIOB.iobContrib) iob += tIOB.iobContrib; - if (tIOB && tIOB.activityContrib) activity += tIOB.activityContrib; - } - }); - return { - iob: iob, - display: Math.round(iob * 100) / 100, - activity: activity - }; + return { + iob: iob, + display: Math.round(iob * 100) / 100, + activity: activity + }; } -function iobCalc(treatment, profile, time) { +function calcTreatment(treatment, profile, time) { - var dia=profile.dia; - var scaleFactor = 3.0/dia; - var peak = 75; - var sens=profile.sens; - var iobContrib, activityContrib; - var t = time; - if (typeof t === 'undefined') { - t = new Date(); - } + var dia = profile.dia + , scaleFactor = 3.0 / dia + , peak = 75 + , sens = profile.sens + , iobContrib = 0 + , activityContrib = 0; + if (treatment.insulin) { + var bolusTime = new Date(treatment.created_at); + var minAgo = scaleFactor * (time - bolusTime) / 1000 / 60; - if (treatment.insulin) { - var bolusTime=new Date(treatment.created_at); - var minAgo=scaleFactor*(t-bolusTime)/1000/60; - - if (minAgo < 0) { - iobContrib=0; - activityContrib=0; - } - if (minAgo < peak) { - var x = minAgo/5+1; - iobContrib=treatment.insulin*(1-0.001852*x*x+0.001852*x); - activityContrib=sens*treatment.insulin*(2/dia/60/peak)*minAgo; - - } - else if (minAgo < 180) { - var x = (minAgo-75)/5; - iobContrib=treatment.insulin*(0.001323*x*x - .054233*x + .55556); - activityContrib=sens*treatment.insulin*(2/dia/60-(minAgo-peak)*2/dia/60/(60*dia-peak)); - } - else { - iobContrib=0; - activityContrib=0; - } - return { - iobContrib: iobContrib, - activityContrib: activityContrib - }; + if (minAgo < peak) { + var x = minAgo / 5 + 1; + iobContrib = treatment.insulin * (1 - 0.001852 * x * x + 0.001852 * x); + activityContrib = sens * treatment.insulin * (2 / dia / 60 / peak) * minAgo; + + } else if (minAgo < 180) { + var x = (minAgo - 75) / 5; + iobContrib = treatment.insulin * (0.001323 * x * x - .054233 * x + .55556); + activityContrib = sens * treatment.insulin * (2 / dia / 60 - (minAgo - peak) * 2 / dia / 60 / (60 * dia - peak)); } else { - return ''; + iobContrib = 0; + activityContrib = 0; } + + } + + return { + iobContrib: iobContrib, + activityContrib: activityContrib + }; + } function IOB(opts) { - var IOB = { - calcTotal: iobTotal - }; - - return IOB; + return { + calcTotal: calcTotal + }; } diff --git a/static/js/client.js b/static/js/client.js index 9962173e2..0905fe297 100644 --- a/static/js/client.js +++ b/static/js/client.js @@ -389,7 +389,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; if (showIOB()) { var iob = Nightscout.iob.calcTotal(treatments, profile, time); - details += ", IOB:" + iob.display; + details += ", IOB: " + iob.display; } currentBG.css('text-decoration','line-through'); @@ -419,7 +419,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; if (showIOB()) { var iob = Nightscout.iob.calcTotal(treatments, profile, nowDate); - details += ", IOB:" + iob.display; + details += ", IOB: " + iob.display; } currentBG.css('text-decoration', ''); From 127e4f5fbbc74b76f6950f464df769246674186e Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 12:29:15 -0800 Subject: [PATCH 11/22] added mocks for treatments and profile so the pebble test doesn't fail --- tests/pebble.test.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/pebble.test.js b/tests/pebble.test.js index 6b3b07430..d6704ed79 100644 --- a/tests/pebble.test.js +++ b/tests/pebble.test.js @@ -75,6 +75,18 @@ var entries = { }; //Mock devicestatus +var treatments = { + list: function(callback) { + callback(null, []); + } +}; + +var profile = { + list: function(callback) { + callback(null, []); + } +}; + var devicestatus = { last: function(callback) { callback(null, {uploaderBattery: 100}); @@ -87,7 +99,7 @@ describe('Pebble Endpoint without Raw', function ( ) { var env = require('../env')( ); this.app = require('express')( ); this.app.enable('api'); - this.app.use('/pebble', pebble(entries, devicestatus, env)); + this.app.use('/pebble', pebble(entries, treatments, profile, devicestatus, env)); done(); }); @@ -128,7 +140,7 @@ describe('Pebble Endpoint with Raw', function ( ) { envRaw.enable = "rawbg"; this.appRaw = require('express')( ); this.appRaw.enable('api'); - this.appRaw.use('/pebble', pebbleRaw(entries, devicestatus, envRaw)); + this.appRaw.use('/pebble', pebbleRaw(entries, treatments, profile, devicestatus, envRaw)); done(); }); From 0aea8a7b26ac01cbb913b609f1f2c179cbf0c3d6 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 1 Mar 2015 13:21:30 -0800 Subject: [PATCH 12/22] very basic IOB test --- lib/iob.js | 2 ++ tests/iob.test.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/iob.test.js diff --git a/lib/iob.js b/lib/iob.js index b9629b2f9..97bca5972 100644 --- a/lib/iob.js +++ b/lib/iob.js @@ -2,6 +2,8 @@ function calcTotal(treatments, profile, time) { + console.info("trying to calc"); + var iob = 0 , activity = 0; diff --git a/tests/iob.test.js b/tests/iob.test.js new file mode 100644 index 000000000..e4dad0fcb --- /dev/null +++ b/tests/iob.test.js @@ -0,0 +1,33 @@ +var should = require('should'); + +describe('IOB', function ( ) { + var iob = require('../lib/iob')(); + + it('should calculate IOB', function() { + + var time = new Date() + , treatments = [ { + created_at: time - 1, + insulin: "1.00" + } + ] + , profile = { + dia: 3, + sens: 0 + }; + + var rightAfterBolus = iob.calcTotal(treatments, profile, time); + + rightAfterBolus.display.should.equal(1); + + var afterSomeTime = iob.calcTotal(treatments, profile, new Date(time.getTime() + (60 * 60 * 1000))); + + afterSomeTime.display.should.be.lessThan(1); + afterSomeTime.display.should.be.greaterThan(0); + + var afterDIA = iob.calcTotal(treatments, profile, new Date(time.getTime() + (3 * 60 * 60 * 1000))); + + afterDIA.display.should.equal(0); + + }); +}); \ No newline at end of file From aa9233be1bad2c7a51f09ff80bfde3226baecfff Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Fri, 6 Mar 2015 00:24:35 -0800 Subject: [PATCH 13/22] new pill display for BG delta and IOB --- static/css/main.css | 58 ++++++++++++++++++++++++++++++++++++++++++----------- static/index.html | 4 ++-- static/js/client.js | 26 +++++++++++++++--------- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/static/css/main.css b/static/css/main.css index ed5ef975b..19768f16e 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -68,21 +68,50 @@ body { .bgStatus .currentDetails { font-size: 25%; - text-decoration: line-through; - display: block; - position: relative; - top: -20px; + position: relative; + top: -10px; } -.bgStatus.current .currentBG { - text-decoration: none; + +.currentDetails > span { + border-radius: 5px; + border: 2px solid #808080; + font-size: 80%; + display: inline-block; } -.bgStatus.current .currentDetails { - font-size: 25%; + +#bgButton .currentDetails > span { + border-color: #000; +} + +.currentDetails > span:not(:first-child) { + margin-left: 5px; +} + +.currentDetails > span * { + padding-left: 2px; + padding-right: 2px; + padding-bottom: 1px; +} + +.currentDetails > span em { + font-style: normal; + font-weight: bold; +} + +.currentDetails > span label { + color: #000; + background: #808080; +} + +#bgButton .currentDetails > span label { + color: #808080; + background: inherit; +} + +.bgStatus.current .currentBG { text-decoration: none; - display: block; - position: relative; - top: -10px; } + .currentDirection { font-weight: normal; text-decoration: none; @@ -101,7 +130,7 @@ body { font-size: 25%; } #lastEntry { - background: #999; + background: #808080; border-radius: 5px; color: #000; padding: 0.25em; @@ -261,6 +290,11 @@ div.tooltip { #currentTime { font-size: 85%; } + + .bgStatus .currentDetails { + top: 0px; + } + } @media (max-width: 750px) { diff --git a/static/index.html b/static/index.html index cee26186d..703c87db5 100644 --- a/static/index.html +++ b/static/index.html @@ -30,13 +30,13 @@

Nightscout

--- - - -- +
--