Created
September 5, 2015 05:04
-
-
Save scottleibrand/b15de1d1e7807023010a to your computer and use it in GitHub Desktop.
Diff of all new code in https://github.com/scottleibrand/cgm-remote-monitor/commits/wip/iob-cob
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/env.js b/env.js | |
index c063c22..9e2c0eb 100644 | |
--- a/env.js | |
+++ b/env.js | |
@@ -43,6 +43,7 @@ function config ( ) { | |
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.pumphistory_collection = readENV('MONGO_PUMPHISTORY_COLLECTION', 'pump'); | |
env.enable = readENV('ENABLE'); | |
env.SSL_KEY = readENV('SSL_KEY'); | |
diff --git a/lib/api/index.js b/lib/api/index.js | |
index 9943061..3cc7eac 100644 | |
--- a/lib/api/index.js | |
+++ b/lib/api/index.js | |
@@ -1,6 +1,6 @@ | |
'use strict'; | |
-function create (env, entries, settings, treatments, profile, devicestatus) { | |
+function create (env, entries, settings, treatments, profile, devicestatus, pumphistory) { | |
var express = require('express'), | |
app = express( ) | |
; | |
@@ -49,6 +49,7 @@ function create (env, entries, settings, treatments, profile, devicestatus) { | |
app.use('/', require('./treatments/')(app, wares, treatments)); | |
app.use('/', require('./profile/')(app, wares, profile)); | |
app.use('/', require('./devicestatus/')(app, wares, devicestatus)); | |
+ app.use('/', require('./pumphistory/')(app, wares, pumphistory)); | |
// Status | |
app.use('/', require('./status')(app, wares)); | |
diff --git a/lib/api/pumphistory/index.js b/lib/api/pumphistory/index.js | |
new file mode 100644 | |
index 0000000..61eb4c9 | |
--- /dev/null | |
+++ b/lib/api/pumphistory/index.js | |
@@ -0,0 +1,47 @@ | |
+'use strict'; | |
+ | |
+var consts = require('../../constants'); | |
+ | |
+function configure (app, wares, pumphistory) { | |
+ 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('/pumphistory/', function(req, res) { | |
+ pumphistory.list({}, function (err, pumphistory) { | |
+ return res.json(pumphistory); | |
+ }); | |
+ }); | |
+ | |
+ function config_authed (app, api, wares, pumphistory) { | |
+ | |
+ api.post('/pumphistory/', /*TODO: auth disabled for now, need to get login figured out... wares.verifyAuthorization, */ function(req, res) { | |
+ var pumprecord = req.body; | |
+ pumphistory.create(pumprecord, 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, pumphistory); | |
+ } | |
+ | |
+ return api; | |
+} | |
+ | |
+module.exports = configure; | |
+ | |
diff --git a/lib/pumphistory.js b/lib/pumphistory.js | |
new file mode 100644 | |
index 0000000..ffafb2e | |
--- /dev/null | |
+++ b/lib/pumphistory.js | |
@@ -0,0 +1,34 @@ | |
+'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 (opts, fn) { | |
+ function find ( ) { | |
+ var q = opts && opts.find ? opts.find : { }; | |
+ return q; | |
+ } | |
+ | |
+ return api( ).find(find()).sort({created_at: -1}).toArray(fn); | |
+ } | |
+ /* | |
+ 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; | |
diff --git a/lib/websocket.js b/lib/websocket.js | |
index e5c3e50..369cfc5 100644 | |
--- a/lib/websocket.js | |
+++ b/lib/websocket.js | |
@@ -1,5 +1,5 @@ | |
-function websocket (env, server, entries, treatments, profiles) { | |
+function websocket (env, server, entries, treatments, profiles, pumphistory) { | |
"use strict"; | |
// CONSTANTS | |
var ONE_HOUR = 3600000, | |
@@ -33,7 +33,8 @@ var dir2Char = { | |
mbgData = [], | |
calData = [], | |
profileData = [], | |
- patientData = []; | |
+ patientData = [], | |
+ pumpData = []; | |
function start ( ) { | |
io = require('socket.io').listen(server); | |
@@ -151,6 +152,7 @@ function update() { | |
treatmentData = []; | |
mbgData = []; | |
profileData = []; | |
+ pumpData = []; | |
var earliest_data = now - dataRange; | |
var q = { find: {"date": {"$gte": earliest_data}} }; | |
@@ -197,16 +199,43 @@ function update() { | |
}); | |
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) { | |
+ // 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( ); | |
+ } | |
+ }); | |
+ var pq = { find: {"date": {"$gte": (earliest_data - (ONE_DAY / 2))}} }; | |
+ /* | |
+ console.log(pq); | |
+ console.log(pumphistory.list); | |
+ pumphistory.list(pq, function (err, results) { | |
+ pumpData = results.map(function(pumpRecord) { | |
+ var timestamp = new Date(pumpRecord.timestamp || pumpRecord.created_at); | |
+ pumpRecord.x = timestamp.getTime(); | |
+ console.log(pumpRecord); | |
+ return pumpRecord; | |
+ }); | |
+ }); | |
+ */ | |
+ pumphistory.list(pq, function (err, results) { | |
+ pumpData = results; | |
+ /* | |
+ console.log(pumpData); | |
+ }); | |
+ pumphistory.list(pq, function (err, results) { | |
+ console.log(results); | |
+ results.forEach(function(element, index, array) { | |
+ console.log(element); | |
+ }); | |
+ */ | |
+ | |
+ // all done, do loadData | |
+ loadData( ); | |
+ | |
+ }); | |
}); | |
}); | |
}); | |
@@ -223,6 +252,7 @@ function loadData() { | |
treatment = [], | |
mbg = [], | |
cal = [], | |
+ pump = [], | |
errorCode; | |
if (cgmData) { | |
@@ -259,6 +289,13 @@ function loadData() { | |
var profile = profileData; | |
} | |
+ if (pumpData) { | |
+ pump = pumpData.slice(pumpData.length-400, pumpData.length); | |
+ pump.sort(function(a, b) { | |
+ return a.date - b.date; | |
+ }); | |
+ } | |
+ | |
if (actualCurrent && actualCurrent < 39) errorCode = actualCurrent; | |
var actualLength = actual.length - 1; | |
@@ -294,8 +331,8 @@ function loadData() { | |
//TODO: need to consider when data being sent has less than the 2 day minimum | |
// consolidate and send the data to the client | |
- var shouldEmit = is_different(actual, predicted, mbg, treatment, cal); | |
- patientData = [actual, predicted, mbg, treatment, profile, cal]; | |
+ var shouldEmit = is_different(actual, predicted, mbg, treatment, cal, pump); | |
+ patientData = [actual, predicted, mbg, treatment, profile, cal, pump]; | |
console.log('patientData', patientData.length); | |
if (shouldEmit) { | |
emitData( ); | |
diff --git a/server.js b/server.js | |
index ef3dc75..95bb292 100644 | |
--- a/server.js | |
+++ b/server.js | |
@@ -38,6 +38,7 @@ var store = require('./lib/storage')(env, function() { | |
console.info("Mongo ready"); | |
entries.ensureIndexes(env.mongo_collection, store); | |
treatments.ensureIndexes(env.treatments_collection, store); | |
+ //pumphistory.ensureIndexes(env.pumphistory_collection, store); | |
devicestatus.ensureIndexes(env.devicestatus_collection, store); | |
}); | |
@@ -52,7 +53,8 @@ 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, profile, devicestatusStorage); | |
+var pumphistory = require('./lib/pumphistory')(env.pumphistory_collection, store); | |
+var api = require('./lib/api/')(env, entriesStorage, settings, treatmentsStorage, profile, devicestatusStorage, pumphistory); | |
var pebble = require('./lib/pebble'); | |
/////////////////////////////////////////////////// | |
@@ -106,7 +108,7 @@ store(function ready ( ) { | |
// setup socket io for data and message transmission | |
/////////////////////////////////////////////////// | |
var websocket = require('./lib/websocket'); | |
- var io = websocket(env, server, entriesStorage, treatmentsStorage, profile); | |
+ var io = websocket(env, server, entriesStorage, treatmentsStorage, profile, pumphistory); | |
}); | |
/////////////////////////////////////////////////// | |
diff --git a/static/js/client.js b/static/js/client.js | |
index 567fbb4..1e82c99 100644 | |
--- a/static/js/client.js | |
+++ b/static/js/client.js | |
@@ -32,6 +32,8 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; | |
, treatments | |
, profile | |
, cal | |
+ , pumpHistory | |
+ , tempTreatments | |
, padding = { top: 20, right: 10, bottom: 30, left: 10 } | |
, opacity = {current: 1, DAY: 1, NIGHT: 0.5} | |
, now = Date.now() | |
@@ -1106,24 +1108,35 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; | |
var CR = treatment.CR || 20; | |
var carbs = treatment.carbs || CR; | |
- var insulin = treatment.insulin || 1; | |
+ var insulin = Math.abs(treatment.insulin) || 1; | |
- var R1 = Math.sqrt(Math.min(carbs, insulin * CR)) / scale, | |
- R2 = Math.sqrt(Math.max(carbs, insulin * CR)) / scale, | |
- R3 = R2 + 8 / scale; | |
+ var R1 = Math.sqrt(Math.min(carbs, insulin * CR)+4) / scale; | |
+ var R2 = Math.sqrt(Math.max(carbs, insulin * CR)+4) / scale; | |
+ var R3 = R2 + 10 / scale; | |
+ var rWhite = 0, rBlue = 0; | |
+ if (treatment.carbs) { rWhite = R1; } | |
+ if (treatment.insulin < 0) { rWhite = R1; } | |
+ if (treatment.insulin > 0) { rBlue = R1; } | |
var arc_data = [ | |
- { 'element': '', 'color': 'white', 'start': -1.5708, 'end': 1.5708, 'inner': 0, 'outer': R1 }, | |
+ { 'element': '', 'color': 'magenta', 'start': -1.5708, 'end': 1.5708, 'inner': 0, 'outer': rWhite }, | |
{ 'element': '', 'color': 'transparent', 'start': -1.5708, 'end': 1.5708, 'inner': R2, 'outer': R3 }, | |
- { 'element': '', 'color': '#0099ff', 'start': 1.5708, 'end': 4.7124, 'inner': 0, 'outer': R1 }, | |
+ { 'element': '', 'color': '#0099ff', 'start': 1.5708, 'end': 4.7124, 'inner': 0, 'outer': rBlue }, | |
{ 'element': '', 'color': 'transparent', 'start': 1.5708, 'end': 4.7124, 'inner': R2, 'outer': R3 } | |
]; | |
arc_data[0].outlineOnly = !treatment.carbs; | |
arc_data[2].outlineOnly = !treatment.insulin; | |
+ // negative boluses should be magenta like carbs | |
+ if (treatment.insulin < 0) { | |
+ arc_data[0].outlineOnly = false; | |
+ arc_data[2].outlineOnly = true; | |
+ } | |
+ | |
if (treatment.carbs > 0) arc_data[1].element = Math.round(treatment.carbs) + ' g'; | |
- if (treatment.insulin > 0) arc_data[3].element = Math.round(treatment.insulin * 100) / 100 + ' U'; | |
+ if (treatment.insulin < -0.1) arc_data[1].element = Math.round(treatment.insulin * 100) / 100 + ' U'; | |
+ if (treatment.insulin > 0.1) arc_data[3].element = Math.round(treatment.insulin * 100) / 100 + ' U'; | |
var arc = d3.svg.arc() | |
.innerRadius(function (d) { return 5 * d.inner; }) | |
@@ -1163,11 +1176,13 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; | |
// labels for carbs and insulin | |
+ var labelColor = 'magenta'; | |
+ if (rWhite == 0 && rBlue > 0) { labelColor = '#0099ff'; } | |
if (showValues) { | |
var label = treatmentDots.append('g') | |
.attr('class', 'path') | |
.attr('id', 'label') | |
- .style('fill', 'white'); | |
+ .style('fill', labelColor); | |
label.append('text') | |
.style('font-size', 30 / scale) | |
.style('font-family', 'Arial') | |
@@ -1383,12 +1398,65 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; | |
if (tIOB && tIOB.activityContrib) activity += tIOB.activityContrib; | |
} | |
}); | |
+ | |
return { | |
iob: iob, | |
activity: activity | |
}; | |
} | |
+ function calcTempTreatments() { | |
+ var tempHistory = []; | |
+ for (var i=0; i < pumpHistory.length; i++) { | |
+ //if(pumpHistory[i].date < time) { | |
+ if (pumpHistory[i]._type == "TempBasal") { | |
+ var rate = pumpHistory[i].rate; | |
+ var date = pumpHistory[i].date; | |
+ if (i>0 && pumpHistory[i-1].date == date && pumpHistory[i-1]._type == "TempBasalDuration") { | |
+ var duration = pumpHistory[i-1]['duration (min)']; | |
+ } else if (i+1<pumpHistory.length && pumpHistory[i+1].date == date && pumpHistory[i+1]._type == "TempBasalDuration") { | |
+ var duration = pumpHistory[i+1]['duration (min)']; | |
+ } else { console.log("No duration found for "+rate+" U/hr basal"+date); } | |
+ var temp = {}; | |
+ temp.rate = rate; | |
+ temp.date = date; | |
+ temp.started_at = new Date(temp.date); | |
+ temp.duration = duration; | |
+ tempHistory.push(temp); | |
+ } | |
+ //} | |
+ }; | |
+ for (var i=0; i+1 < tempHistory.length; i++) { | |
+ if (tempHistory[i].date + tempHistory[i].duration*60*1000 > tempHistory[i+1].date) { | |
+ tempHistory[i].duration = (tempHistory[i+1].date - tempHistory[i].date)/60/1000; | |
+ } | |
+ } | |
+ var tempBoluses = []; | |
+ var tempBolusSize; | |
+ for (var i=0; i < tempHistory.length; i++) { | |
+ if (tempHistory[i].duration > 0) { | |
+ var netBasalRate = tempHistory[i].rate-profile.basal; | |
+ if (netBasalRate < 0) { tempBolusSize = -0.1; } | |
+ else { tempBolusSize = 0.1; } | |
+ var netBasalAmount = Math.round(netBasalRate*tempHistory[i].duration*10/6)/100 | |
+ var tempBolusCount = Math.round(netBasalAmount / tempBolusSize); | |
+ var tempBolusSpacing = tempHistory[i].duration / tempBolusCount; | |
+ for (var j=0; j < tempBolusCount; j++) { | |
+ var tempBolus = {}; | |
+ tempBolus.insulin = tempBolusSize; | |
+ tempBolus.date = tempHistory[i].date + j * tempBolusSpacing*60*1000; | |
+ tempBolus.created_at = new Date(tempBolus.date); | |
+ tempBoluses.push(tempBolus); | |
+ } | |
+ } | |
+ } | |
+ return { | |
+ tempBoluses: tempBoluses, | |
+ tempHistory: tempHistory | |
+ }; | |
+ | |
+ } | |
+ | |
function cobTotal(treatments, time) { | |
var liverSensRatio = 1; | |
var sens = profile.sens; | |
@@ -1447,7 +1515,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; | |
function carbImpact(rawCarbImpact, insulinImpact) { | |
var liverSensRatio = 1.0; | |
var liverCarbImpactMax = 0.7; | |
- var liverCarbImpact = Math.min(liverCarbImpactMax, liverSensRatio*insulinImpact); | |
+ var liverCarbImpact = Math.max(0,Math.min(liverCarbImpactMax, liverSensRatio*insulinImpact)); | |
//var liverCarbImpact = liverSensRatio*insulinImpact; | |
var netCarbImpact = Math.max(0, rawCarbImpact-liverCarbImpact); | |
var totalImpact = netCarbImpact - insulinImpact; | |
@@ -1696,6 +1764,10 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; | |
profile = d[4][0]; | |
cal = d[5][d[5].length-1]; | |
+ pumpHistory = d[6]; | |
+ tempTreatments = calcTempTreatments().tempBoluses; | |
+ treatments = treatments.concat(tempTreatments); | |
+ | |
var temp1 = [ ]; | |
if (cal && showRawBGs()) { | |
temp1 = d[0].map(function (entry) { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment