From 063dd6ca738b354f2cc4585c937eaa972a7ed5db Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sun, 27 Sep 2015 15:28:19 -0700 Subject: [PATCH 01/27] update devicestatus to be more consistent with entries now that the old direct mongo upload is gone we can use a query to prevent the sorting of all data --- lib/devicestatus.js | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/devicestatus.js b/lib/devicestatus.js index f82db4c5ee2..6dd5eccb9ac 100644 --- a/lib/devicestatus.js +++ b/lib/devicestatus.js @@ -1,5 +1,7 @@ 'use strict'; +var find_options = require('./query'); + function storage (collection, ctx) { var ObjectID = require('mongodb').ObjectID; @@ -13,7 +15,7 @@ function storage (collection, ctx) { } function last(fn) { - return api().find({}).sort({created_at: -1}).limit(1).toArray(function (err, entries) { + return list({count: 1}, function (err, entries) { if (entries && entries.length > 0) { fn(err, entries[0]); } else { @@ -22,9 +24,42 @@ function storage (collection, ctx) { }); } + var with_collection = ctx.store.with_collection(collection); + + function query_for (opts) { + return find_options(opts, storage.queryOpts); + } + function list(opts, fn) { - var q = opts && opts.find ? opts.find : { }; - return ctx.store.limit.call(api().find(q).sort({created_at: -1}), opts).toArray(fn); + with_collection(function (err, collection) { + // these functions, find, sort, and limit, are used to + // dynamically configure the request, based on the options we've + // been given + + // determine sort options + function sort ( ) { + return opts && opts.sort || {created_at: -1}; + } + + // configure the limit portion of the current query + function limit ( ) { + if (opts && opts.count) { + return this.limit(parseInt(opts.count)); + } + return this; + } + + // handle all the results + function toArray (err, entries) { + fn(err, entries); + } + + // now just stitch them all together + limit.call(collection + .find(query_for(opts)) + .sort(sort( )) + ).toArray(toArray); + }); } function remove (_id, fn) { @@ -49,4 +84,8 @@ function storage (collection, ctx) { return api; } +storage.queryOpts = { + dateField: 'created_at' +}; + module.exports = storage; From be38f91d5fd89bdbace063b60e9d8e35ec668a47 Mon Sep 17 00:00:00 2001 From: Ben West Date: Thu, 8 Oct 2015 17:33:05 -0700 Subject: [PATCH 02/27] specify shorter connecti timeout Hat tip to @oamyoamy See also: * http://blog.mongolab.com/2013/10/do-you-want-a-timeout/ --- lib/storage.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/storage.js b/lib/storage.js index 30bfd60b0b1..322034a506c 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -24,7 +24,10 @@ function init (env, cb, forceNewConnection) { } console.log('Setting up new connection to MongoDB'); - MongoClient.connect(env.mongo, function connected(err, db) { + var timeout = 60 * 2.5 * 1000; + var options = { replset: { socketOptions: { connectTimeoutMS : timeout, socketTimeoutMS : timeout }}}; + + MongoClient.connect(env.mongo, options, function connected(err, db) { if (err) { console.log('Error connecting to MongoDB, ERROR: %j', err); throw err; @@ -75,4 +78,4 @@ function init (env, cb, forceNewConnection) { return maybe_connect(cb); } -module.exports = init; \ No newline at end of file +module.exports = init; From c0697d0a987f0572826f42bb62761589e799de2a Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Sun, 4 Oct 2015 20:25:42 +0200 Subject: [PATCH 03/27] duration in careportal --- lib/client/careportal.js | 42 ++++++++++++++++++++++ lib/client/renderer.js | 61 +++++++++++++++++++++++++++++--- lib/language.js | 20 +++++++---- lib/report_plugins/treatments.js | 16 +++++++-- lib/treatments.js | 2 ++ static/index.html | 12 ++++--- 6 files changed, 135 insertions(+), 18 deletions(-) diff --git a/lib/client/careportal.js b/lib/client/careportal.js index 3d153e5ee42..100e9b4e074 100644 --- a/lib/client/careportal.js +++ b/lib/client/careportal.js @@ -50,11 +50,50 @@ function init (client, $) { } } + careportal.filterInputs = function filterInputs ( event ) { + var inputMatrix = { + 'BG Check': { bg: true, insulin: false, carbs: false, prebolus: false, duration: false } + , 'Snack Bolus': { bg: true, insulin: true, carbs: true, prebolus: true, duration: false } + , 'Meal Bolus': { bg: true, insulin: true, carbs: true, prebolus: true, duration: false } + , 'Correction Bolus': { bg: true, insulin: true, carbs: false, prebolus: false, duration: false } + , 'Carb Correction': { bg: true, insulin: false, carbs: true, prebolus: false, duration: false } + , 'Announcement': { bg: false, insulin: false, carbs: false, prebolus: false, duration: false } + , 'Note': { bg: true, insulin: false, carbs: false, prebolus: false, duration: true } + , 'Question': { bg: true, insulin: false, carbs: false, prebolus: false, duration: false } + , 'Exercise': { bg: false, insulin: false, carbs: false, prebolus: false, duration: true } + , 'Site Change': { bg: false, insulin: false, carbs: false, prebolus: false, duration: false } + , 'Sensor Start': { bg: false, insulin: false, carbs: false, prebolus: false, duration: false } + , 'Sensor Change': { bg: false, insulin: false, carbs: false, prebolus: false, duration: false } + , 'Insulin Change': { bg: false, insulin: false, carbs: false, prebolus: false, duration: false } + , 'D.A.D. Alert': { bg: true, insulin: false, carbs: false, prebolus: false, duration: false } + }; + + var eventType = $('#eventType').val(); + + function displayType (enabled) { + if (enabled) { + return ''; + } else { + return 'none'; + } + } + + $('#bg').css('display',displayType(inputMatrix[eventType]['bg'])); + $('#insulinGivenLabel').css('display',displayType(inputMatrix[eventType]['insulin'])); + $('#carbsGivenLabel').css('display',displayType(inputMatrix[eventType]['carbs'])); + $('#durationLabel').css('display',displayType(inputMatrix[eventType]['duration'])); + $('#preBolusLabel').css('display',displayType(inputMatrix[eventType]['prebolus'])); + + maybePrevent(event); + }; + careportal.prepareEvents = function prepareEvents ( ) { $('#eventType').empty(); _.each(careportal.events, function eachEvent(event) { $('#eventType').append(''); }); + $('#eventType').change(careportal.filterInputs); + careportal.filterInputs(); }; careportal.resolveEventName = function resolveEventName(value) { @@ -73,6 +112,7 @@ function init (client, $) { $('#meter').prop('checked', true); $('#carbsGiven').val(''); $('#insulinGiven').val(''); + $('#duration').val(''); $('#preBolus').val(0); $('#notes').val(''); $('#enteredBy').val(storage.get('enteredBy') || ''); @@ -88,6 +128,7 @@ function init (client, $) { , glucoseType: $('#treatment-form').find('input[name=glucoseType]:checked').val() , carbs: $('#carbsGiven').val() , insulin: $('#insulinGiven').val() + , duration: $('#duration').val() , preBolus: parseInt($('#preBolus').val()) , notes: $('#notes').val() , units: client.settings.units @@ -123,6 +164,7 @@ function init (client, $) { pushIf(data.carbs, translate('Carbs Given') + ': ' + data.carbs); pushIf(data.insulin, translate('Insulin Given') + ': ' + data.insulin); + pushIf(data.duration, translate('Duration') + ': ' + data.duration); pushIf(data.preBolus, translate('Carb Time') + ': ' + data.preBolus + ' ' + translate('mins')); pushIf(data.notes, translate('Notes') + ': ' + data.notes); pushIf(data.enteredBy, translate('Entered By') + ': ' + data.enteredBy); diff --git a/lib/client/renderer.js b/lib/client/renderer.js index c475c0815cd..c8f4d0c4fd1 100644 --- a/lib/client/renderer.js +++ b/lib/client/renderer.js @@ -167,7 +167,7 @@ function init (client, d3) { renderer.addTreatmentCircles = function addTreatmentCircles ( ) { function treatmentTooltip (d) { return ''+translate('Time')+': ' + client.formatTime(new Date(d.mills)) + '
' + - (d.eventType ? ''+translate('Treatment type')+': ' + d.eventType + '
' : '') + + (d.eventType ? ''+translate('Treatment type')+': ' + translate(d.eventType) + '
' : '') + (d.glucose ? ''+translate('BG')+': ' + d.glucose + (d.glucoseType ? ' (' + translate(d.glucoseType) + ')': '') + '
' : '') + (d.enteredBy ? ''+translate('Entered By')+': ' + d.enteredBy + '
' : '') + (d.notes ? ''+translate('Notes')+': ' + d.notes : ''); @@ -182,8 +182,8 @@ function init (client, d3) { //NOTE: treatments with insulin or carbs are drawn by drawTreatment() // bind up the focus chart data to an array of circles - var treatCircles = chart().focus.selectAll('rect').data(client.treatments.filter(function(treatment) { - return !treatment.carbs && !treatment.insulin; + var treatCircles = chart().focus.selectAll('treatment-dot').data(client.treatments.filter(function(treatment) { + return !treatment.carbs && !treatment.insulin && !treatment.duration; })); function prepareTreatCircles(sel) { @@ -237,6 +237,59 @@ function init (client, d3) { .on('mouseout', hideTooltip); treatCircles.attr('clip-path', 'url(#clip)'); + + // treatments with duration + var treatRects = chart().focus.selectAll('rect').data(client.treatments.filter(function(treatment) { + return !treatment.carbs && !treatment.insulin && treatment.duration; + })); + + function prepareTreatRects(sel) { + function fillColor(d) { + // this is going to be updated by Event Type + var color = 'grey'; + if (d.eventType === 'Exercise') { + color = 'Violet'; + } else if (d.eventType === 'Note') { + color = 'Salmon'; + } + return color; + } + + sel.attr('x', function (d) { + return chart().xScale(new Date(d.mills)); + }) + .attr('y', function (d) { + return 10; + }) + .attr('width', function (d) { + return chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(new Date(d.mills)); + }) + .attr('height', 20) + .attr('rx', 5) + .attr('ry', 5) + .attr('stroke-width', 2) + .attr('opacity', .4) + .attr('stroke', 'white') + .attr('fill', fillColor); + + return sel; + } + + // if already existing then transition each rect to its new position + prepareTreatRects(treatRects.transition()); + + // if new rect then just display + prepareTreatRects(treatRects.enter().append('rect')) + .on('mouseover', function (d) { + client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9); + client.tooltip.html(d.isAnnouncement ? announcementTooltip(d) : treatmentTooltip(d)) + .style('left', (d3.event.pageX) + 'px') + .style('top', (d3.event.pageY + 15) + 'px'); + }) + .on('mouseout', hideTooltip); + + treatRects.attr('clip-path', 'url(#clip)'); + }; renderer.addContextCircles = function addContextCircles ( ) { @@ -348,7 +401,7 @@ function init (client, d3) { .style('top', (d3.event.pageY + 15) + 'px'); } - var treatmentDots = chart().focus.selectAll('treatment-dot') + var treatmentDots = chart().focus.selectAll('treatment-insulincarbs') .data(arc.data) .enter() .append('g') diff --git a/lib/language.js b/lib/language.js index 74fb09f704d..b3bb023b628 100644 --- a/lib/language.js +++ b/lib/language.js @@ -3103,13 +3103,6 @@ function init() { ,nb: 'Spørsmål' ,he: 'שאלה' } - ,'Announcement' : { - bg: 'Известяване' - , fi: 'Tiedoitus' - ,pt: 'Aviso' - ,ro: 'Anunț' - ,he: 'הודעה' - } ,'Exercise' : { cs: 'Cvičení' ,de: 'Bewegung' @@ -4252,6 +4245,19 @@ function init() { ,ro: 'Modifică înregistrarea' ,bg: 'Редакция на събитие' } + ,'Duration' : { + cs: 'Doba trvání' + } + ,'Duration in minutes' : { + cs: 'Doba trvání v minutách' + } + ,'Announcement' : { + cs: 'Oznámení' + ,bg: 'Известяване' + ,fi: 'Tiedoitus' + ,pt: 'Aviso' + ,he: 'הודעה' + } }; diff --git a/lib/report_plugins/treatments.js b/lib/report_plugins/treatments.js index 5f385c2b8f7..9cac078d55e 100644 --- a/lib/report_plugins/treatments.js +++ b/lib/report_plugins/treatments.js @@ -48,6 +48,11 @@ treatments.html = function html(client) { + ' ' + ' ' + '
' + + ' ' + + '
' + ' ' + '
' + ' -
+
Glucose Reading @@ -192,15 +192,19 @@ Sensor
-