mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
Revert "Revert to c1b2988eb0 (Merge branch 'hotfix/0.3.5')"
This reverts commit 1cc9c6127c.
This commit is contained in:
+4
-1
@@ -8,5 +8,8 @@ my.env
|
|||||||
*.env
|
*.env
|
||||||
static/bower_components/
|
static/bower_components/
|
||||||
.*.sw?
|
.*.sw?
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
.vagrant
|
.vagrant
|
||||||
|
/iisnode
|
||||||
|
/web.config
|
||||||
@@ -5,6 +5,8 @@ cgm-remote-monitor (a.k.a. NightScout)
|
|||||||
[](https://david-dm.org/nightscout/cgm-remote-monitor)
|
[](https://david-dm.org/nightscout/cgm-remote-monitor)
|
||||||
[](https://gitter.im/nightscout/public)
|
[](https://gitter.im/nightscout/public)
|
||||||
|
|
||||||
|
[](https://heroku.com/deploy)
|
||||||
|
|
||||||
This acts as a web-based CGM (Continuous Glucose Montinor) to allow
|
This acts as a web-based CGM (Continuous Glucose Montinor) to allow
|
||||||
multiple caregivers to remotely view a patients glucose data in
|
multiple caregivers to remotely view a patients glucose data in
|
||||||
realtime. The server reads a MongoDB which is intended to be data
|
realtime. The server reads a MongoDB which is intended to be data
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "CGM Remote Monitor",
|
||||||
|
"repository": "https://github.com/nightscout/cgm-remote-monitor",
|
||||||
|
"env": {
|
||||||
|
"MONGO_COLLECTION": {
|
||||||
|
"description": "The mongo collection to connect to.",
|
||||||
|
"value": "nightscout"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"addons": [
|
||||||
|
"mongolab"
|
||||||
|
]
|
||||||
|
}
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
curl -H "Content-Type: application/json" -XPOST 'http://localhost:1337/api/v1/treatments/' -d '{
|
||||||
|
"enteredBy": "Dad",
|
||||||
|
"eventType":"Site Change",
|
||||||
|
"glucoseValue": 322,
|
||||||
|
"glucoseType": "sensor",
|
||||||
|
"carbsGiven": 0,
|
||||||
|
"insulinGiven": 1.25,
|
||||||
|
"notes": "Argh..."
|
||||||
|
}'
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
"name": "nightscout",
|
"name": "nightscout",
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"angularjs": "1.3.0-beta.19",
|
||||||
|
"bootstrap": "~3.2.0",
|
||||||
"d3": "3.4.3",
|
"d3": "3.4.3",
|
||||||
"jquery": "2.1.0",
|
"jquery": "2.1.0",
|
||||||
"jQuery-Storage-API": "~1.7.2",
|
"jQuery-Storage-API": "~1.7.2",
|
||||||
|
|||||||
@@ -23,9 +23,10 @@ function config ( ) {
|
|||||||
env.name = software.name;
|
env.name = software.name;
|
||||||
env.DISPLAY_UNITS = process.env.DISPLAY_UNITS || 'mg/dl';
|
env.DISPLAY_UNITS = process.env.DISPLAY_UNITS || 'mg/dl';
|
||||||
env.PORT = process.env.PORT || 1337;
|
env.PORT = process.env.PORT || 1337;
|
||||||
env.mongo = process.env.MONGO_CONNECTION || process.env.CUSTOMCONNSTR_mongo;
|
env.mongo = process.env.MONGO_CONNECTION || process.env.CUSTOMCONNSTR_mongo || process.env.MONGOLAB_URI;
|
||||||
env.mongo_collection = process.env.CUSTOMCONNSTR_mongo_collection || 'entries';
|
env.mongo_collection = process.env.CUSTOMCONNSTR_mongo_collection || process.env.MONGO_COLLECTION || 'entries';
|
||||||
env.settings_collection = process.env.CUSTOMCONNSTR_mongo_settings_collection || 'settings';
|
env.settings_collection = process.env.CUSTOMCONNSTR_mongo_settings_collection || 'settings';
|
||||||
|
env.treatments_collection = process.env.CUSTOMCONNSTR_mongo_treatments_collection || 'treatments';
|
||||||
var shasum = crypto.createHash('sha1');
|
var shasum = crypto.createHash('sha1');
|
||||||
var useSecret = (process.env.API_SECRET && process.env.API_SECRET.length > 0);
|
var useSecret = (process.env.API_SECRET && process.env.API_SECRET.length > 0);
|
||||||
env.api_secret = null;
|
env.api_secret = null;
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function create (env, entries, settings) {
|
function create (env, entries, settings, treatments) {
|
||||||
var express = require('express'),
|
var express = require('express'),
|
||||||
app = express( )
|
app = express( )
|
||||||
;
|
;
|
||||||
@@ -29,6 +29,7 @@ function create (env, entries, settings) {
|
|||||||
// Entries and settings
|
// Entries and settings
|
||||||
app.use('/', require('./entries/')(app, wares, entries));
|
app.use('/', require('./entries/')(app, wares, entries));
|
||||||
app.use('/', require('./settings/')(app, wares, settings));
|
app.use('/', require('./settings/')(app, wares, settings));
|
||||||
|
app.use('/', require('./treatments/')(app, wares, treatments));
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
app.use('/', require('./status')(app, wares));
|
app.use('/', require('./status')(app, wares));
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var consts = require('../../constants');
|
||||||
|
|
||||||
|
function configure (app, wares, treatments) {
|
||||||
|
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('/treatments/', function(req, res) {
|
||||||
|
treatments.list(function (err, profiles) {
|
||||||
|
return res.json(profiles);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function config_authed (app, api, wares, treatments) {
|
||||||
|
|
||||||
|
api.post('/treatments/', /*TODO: auth disabled for quick UI testing... wares.verifyAuthorization, */ function(req, res) {
|
||||||
|
var treatment = req.body;
|
||||||
|
treatments.create(treatment, function (err, created) {
|
||||||
|
if (err)
|
||||||
|
res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
|
||||||
|
else
|
||||||
|
res.json(created);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.enabled('api') || true /*TODO: auth disabled for quick UI testing...*/) {
|
||||||
|
config_authed(app, api, wares, treatments);
|
||||||
|
}
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = configure;
|
||||||
|
|
||||||
+9
-3
@@ -92,10 +92,16 @@ function entries (name, storage) {
|
|||||||
with_collection(function(err, collection) {
|
with_collection(function(err, collection) {
|
||||||
if (err) { fn(err); return; }
|
if (err) { fn(err); return; }
|
||||||
// potentially a batch insert
|
// potentially a batch insert
|
||||||
collection.insert(docs, function (err, created) {
|
var firstErr = null,
|
||||||
// execute the callback
|
totalCreated = 0;
|
||||||
fn(err, created, docs);
|
|
||||||
|
docs.forEach(function(doc) {
|
||||||
|
collection.update(doc, doc, {upsert: true}, function (err, created) {
|
||||||
|
firstErr = firstErr || err;
|
||||||
|
totalCreated += created;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
fn(firstErr, totalCreated, docs);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
+38
-12
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
function websocket (env, server, entries) {
|
function websocket (env, server, entries, treatments) {
|
||||||
"use strict";
|
"use strict";
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
var ONE_HOUR = 3600000,
|
var ONE_HOUR = 3600000,
|
||||||
@@ -24,8 +24,10 @@ var dir2Char = {
|
|||||||
var io;
|
var io;
|
||||||
var watchers = 0;
|
var watchers = 0;
|
||||||
var now = new Date().getTime();
|
var now = new Date().getTime();
|
||||||
var cgmData = [];
|
|
||||||
var patientData = [];
|
var cgmData = [],
|
||||||
|
treatmentData = [],
|
||||||
|
patientData = [];
|
||||||
|
|
||||||
function start ( ) {
|
function start ( ) {
|
||||||
io = require('socket.io').listen(server);
|
io = require('socket.io').listen(server);
|
||||||
@@ -101,6 +103,7 @@ function update() {
|
|||||||
now = Date.now();
|
now = Date.now();
|
||||||
|
|
||||||
cgmData = [];
|
cgmData = [];
|
||||||
|
treatmentData = [];
|
||||||
var earliest_data = now - TWO_DAYS;
|
var earliest_data = now - TWO_DAYS;
|
||||||
var q = { find: {"date": {"$gte": earliest_data}} };
|
var q = { find: {"date": {"$gte": earliest_data}} };
|
||||||
entries.list(q, function (err, results) {
|
entries.list(q, function (err, results) {
|
||||||
@@ -114,8 +117,15 @@ function update() {
|
|||||||
cgmData.push(obj);
|
cgmData.push(obj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// all done, do loadData
|
treatments.list(function (err, results) {
|
||||||
loadData( );
|
treatmentData = results.map(function(treatment) {
|
||||||
|
var timestamp = new Date(treatment.timestamp || treatment.created_at);
|
||||||
|
treatment.x = timestamp.getTime();
|
||||||
|
return treatment;
|
||||||
|
});
|
||||||
|
// all done, do loadData
|
||||||
|
loadData( );
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return update;
|
return update;
|
||||||
@@ -133,16 +143,21 @@ function emitAlarm(alarmType) {
|
|||||||
function loadData() {
|
function loadData() {
|
||||||
|
|
||||||
console.log('running loadData');
|
console.log('running loadData');
|
||||||
var treatment = [];
|
|
||||||
var mbg = [];
|
var mbg = [];
|
||||||
|
|
||||||
var actual = [];
|
var actual = [],
|
||||||
|
actualCurrent,
|
||||||
|
treatment = [],
|
||||||
|
errorCode;
|
||||||
|
|
||||||
if (cgmData) {
|
if (cgmData) {
|
||||||
actual = cgmData.slice();
|
actual = cgmData.slice();
|
||||||
actual.sort(function(a, b) {
|
actual.sort(function(a, b) {
|
||||||
return a.x - b.x;
|
return a.x - b.x;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
actualCurrent = actual.length > 0 ? actual[actual.length - 1].y : null;
|
||||||
|
|
||||||
// sgv less than or equal to 10 means error code
|
// sgv less than or equal to 10 means error code
|
||||||
// or warm up period code, so ignore
|
// or warm up period code, so ignore
|
||||||
actual = actual.filter(function (a) {
|
actual = actual.filter(function (a) {
|
||||||
@@ -150,6 +165,15 @@ function loadData() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (treatmentData) {
|
||||||
|
treatment = treatmentData.slice();
|
||||||
|
treatment.sort(function(a, b) {
|
||||||
|
return a.x - b.x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actualCurrent && actualCurrent < 39) errorCode = actualCurrent;
|
||||||
|
|
||||||
var actualLength = actual.length - 1;
|
var actualLength = actual.length - 1;
|
||||||
|
|
||||||
if (actualLength > 1) {
|
if (actualLength > 1) {
|
||||||
@@ -181,8 +205,8 @@ function loadData() {
|
|||||||
//TODO: need to consider when data being sent has less than the 2 day minimum
|
//TODO: need to consider when data being sent has less than the 2 day minimum
|
||||||
|
|
||||||
// consolidate and send the data to the client
|
// consolidate and send the data to the client
|
||||||
var shouldEmit = is_different(actual, predicted, mbg, treatment);
|
var shouldEmit = is_different(actual, predicted, mbg, treatment, errorCode);
|
||||||
patientData = [actual, predicted, mbg, treatment];
|
patientData = [actual, predicted, mbg, treatment, errorCode];
|
||||||
console.log('patientData', patientData.length);
|
console.log('patientData', patientData.length);
|
||||||
if (shouldEmit) {
|
if (shouldEmit) {
|
||||||
emitData( );
|
emitData( );
|
||||||
@@ -195,16 +219,16 @@ function loadData() {
|
|||||||
avgLoss += 1 / size * Math.pow(log10(predicted[j].y / 120), 2);
|
avgLoss += 1 / size * Math.pow(log10(predicted[j].y / 120), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(alarms['urgent_alarm'].threshold);
|
|
||||||
//console.log(alarms['alarm'].threshold);
|
|
||||||
if (avgLoss > alarms['urgent_alarm'].threshold) {
|
if (avgLoss > alarms['urgent_alarm'].threshold) {
|
||||||
emitAlarm('urgent_alarm');
|
emitAlarm('urgent_alarm');
|
||||||
} else if (avgLoss > alarms['alarm'].threshold) {
|
} else if (avgLoss > alarms['alarm'].threshold) {
|
||||||
emitAlarm('alarm');
|
emitAlarm('alarm');
|
||||||
|
} else if (errorCode) {
|
||||||
|
emitAlarm('urgent_alarm');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function is_different (actual, predicted, mbg, treatment) {
|
function is_different (actual, predicted, mbg, treatment, errorCode) {
|
||||||
if (patientData && patientData.length < 3) {
|
if (patientData && patientData.length < 3) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -213,12 +237,14 @@ function loadData() {
|
|||||||
, predicted: patientData[1].slice(-1).pop( )
|
, predicted: patientData[1].slice(-1).pop( )
|
||||||
, mbg: patientData[2].slice(-1).pop( )
|
, mbg: patientData[2].slice(-1).pop( )
|
||||||
, treatment: patientData[3].slice(-1).pop( )
|
, treatment: patientData[3].slice(-1).pop( )
|
||||||
|
, errorCode: patientData.length >= 5 ? patientData[4] : 0
|
||||||
};
|
};
|
||||||
var last = {
|
var last = {
|
||||||
actual: actual.slice(-1).pop( )
|
actual: actual.slice(-1).pop( )
|
||||||
, predicted: predicted.slice(-1).pop( )
|
, predicted: predicted.slice(-1).pop( )
|
||||||
, mbg: mbg.slice(-1).pop( )
|
, mbg: mbg.slice(-1).pop( )
|
||||||
, treatment: treatment.slice(-1).pop( )
|
, treatment: treatment.slice(-1).pop( )
|
||||||
|
, errorCode: errorCode
|
||||||
};
|
};
|
||||||
|
|
||||||
// textual diff of objects
|
// textual diff of objects
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
"express": "^4.6.1",
|
"express": "^4.6.1",
|
||||||
"express-extension-to-accept": "0.0.2",
|
"express-extension-to-accept": "0.0.2",
|
||||||
"mongodb": "^1.4.7",
|
"mongodb": "^1.4.7",
|
||||||
|
"moment": "2.8.1",
|
||||||
"sgvdata": "0.0.2",
|
"sgvdata": "0.0.2",
|
||||||
"socket.io": "^0.9.17"
|
"socket.io": "^0.9.17"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ var express = require('express');
|
|||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
var entries = require('./lib/entries')(env.mongo_collection, store);
|
var entries = require('./lib/entries')(env.mongo_collection, store);
|
||||||
var settings = require('./lib/settings')(env.settings_collection, store);
|
var settings = require('./lib/settings')(env.settings_collection, store);
|
||||||
var api = require('./lib/api/')(env, entries, settings);
|
var treatments = require('./lib/treatments')(env.treatments_collection, store);
|
||||||
|
var api = require('./lib/api/')(env, entries, settings, treatments);
|
||||||
var pebble = require('./lib/pebble');
|
var pebble = require('./lib/pebble');
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -39,7 +40,6 @@ var pebble = require('./lib/pebble');
|
|||||||
// setup http server
|
// setup http server
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
var PORT = env.PORT;
|
var PORT = env.PORT;
|
||||||
var THIRTY_DAYS = 2592000;
|
|
||||||
|
|
||||||
var app = express();
|
var app = express();
|
||||||
var appInfo = software.name + ' ' + software.version;
|
var appInfo = software.name + ' ' + software.version;
|
||||||
@@ -57,7 +57,8 @@ app.get('/pebble', pebble(entries));
|
|||||||
//app.get('/package.json', software);
|
//app.get('/package.json', software);
|
||||||
|
|
||||||
// define static server
|
// define static server
|
||||||
var staticFiles = express.static(env.static_files, {maxAge: THIRTY_DAYS * 1000});
|
//TODO: JC - changed cache to 1 hour from 30d ays to bypass cache hell until we have a real solution
|
||||||
|
var staticFiles = express.static(env.static_files, {maxAge: 60 * 60 * 1000});
|
||||||
|
|
||||||
// serve the static content
|
// serve the static content
|
||||||
app.use(staticFiles);
|
app.use(staticFiles);
|
||||||
@@ -76,7 +77,7 @@ store(function ready ( ) {
|
|||||||
// setup socket io for data and message transmission
|
// setup socket io for data and message transmission
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
var websocket = require('./lib/websocket');
|
var websocket = require('./lib/websocket');
|
||||||
var io = websocket(env, server, entries);
|
var io = websocket(env, server, entries, treatments);
|
||||||
});
|
});
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
|
|||||||
+27
-2
@@ -17,6 +17,31 @@
|
|||||||
#drawer i {
|
#drawer i {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#treatmentDrawer {
|
||||||
|
background-color: #666;
|
||||||
|
border-left: 1px solid #999;
|
||||||
|
box-shadow: inset 4px 4px 5px 0px rgba(50, 50, 50, 0.75);
|
||||||
|
color: #eee;
|
||||||
|
display: none;
|
||||||
|
font-size: 16px;
|
||||||
|
height: calc(100% - 45px);
|
||||||
|
overflow-y: auto;
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 45px;
|
||||||
|
right: -200px;
|
||||||
|
width: 300px;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
#treatmentDrawer i {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treatmentDrawer a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
#about {
|
#about {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
@@ -98,7 +123,7 @@ h1, legend,
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
float: right;
|
float: right;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
width: 180px;
|
width: 190px;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
@@ -118,7 +143,7 @@ h1, legend,
|
|||||||
#buttonbar a {
|
#buttonbar a {
|
||||||
float: left;
|
float: left;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
width: 44px;
|
width: 34px;
|
||||||
}
|
}
|
||||||
#buttonbar i {
|
#buttonbar i {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
|||||||
+1
-1
@@ -136,7 +136,7 @@ body {
|
|||||||
|
|
||||||
#bgButton,
|
#bgButton,
|
||||||
#silenceBtn {
|
#silenceBtn {
|
||||||
z-index: 999;
|
z-index: 99;
|
||||||
}
|
}
|
||||||
|
|
||||||
#bgButton {
|
#bgButton {
|
||||||
|
|||||||
@@ -60,6 +60,12 @@
|
|||||||
"code": 59392,
|
"code": 59392,
|
||||||
"src": "mfglabs"
|
"src": "mfglabs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"uid": "55e2ff85b1c459c383f46da6e96014b0",
|
||||||
|
"css": "plus",
|
||||||
|
"code": 59403,
|
||||||
|
"src": "elusive"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"uid": "b59b3b618699b467541f631edd5a02ed",
|
"uid": "b59b3b618699b467541f631edd5a02ed",
|
||||||
"css": "volume",
|
"css": "volume",
|
||||||
|
|||||||
Vendored
+2
-1
@@ -9,4 +9,5 @@
|
|||||||
.icon-battery-75:before { content: '\e807'; } /* '' */
|
.icon-battery-75:before { content: '\e807'; } /* '' */
|
||||||
.icon-battery-100:before { content: '\e808'; } /* '' */
|
.icon-battery-100:before { content: '\e808'; } /* '' */
|
||||||
.icon-cancel-circled:before { content: '\e809'; } /* '' */
|
.icon-cancel-circled:before { content: '\e809'; } /* '' */
|
||||||
.icon-volume:before { content: '\e80a'; } /* '' */
|
.icon-volume:before { content: '\e80a'; } /* '' */
|
||||||
|
.icon-plus:before { content: '\e80b'; } /* '' */
|
||||||
+8
-7
File diff suppressed because one or more lines are too long
+2
-1
@@ -9,4 +9,5 @@
|
|||||||
.icon-battery-75 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-battery-75 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-battery-100 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-battery-100 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-cancel-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-cancel-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-volume { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-volume { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
Vendored
+2
-1
@@ -20,4 +20,5 @@
|
|||||||
.icon-battery-75 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-battery-75 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-battery-100 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-battery-100 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-cancel-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-cancel-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-volume { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-volume { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
Vendored
+8
-7
@@ -1,10 +1,10 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('../font/fontello.eot?77167544');
|
src: url('../font/fontello.eot?48374311');
|
||||||
src: url('../font/fontello.eot?77167544#iefix') format('embedded-opentype'),
|
src: url('../font/fontello.eot?48374311#iefix') format('embedded-opentype'),
|
||||||
url('../font/fontello.woff?77167544') format('woff'),
|
url('../font/fontello.woff?48374311') format('woff'),
|
||||||
url('../font/fontello.ttf?77167544') format('truetype'),
|
url('../font/fontello.ttf?48374311') format('truetype'),
|
||||||
url('../font/fontello.svg?77167544#fontello') format('svg');
|
url('../font/fontello.svg?48374311#fontello') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('../font/fontello.svg?77167544#fontello') format('svg');
|
src: url('../font/fontello.svg?48374311#fontello') format('svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -60,4 +60,5 @@
|
|||||||
.icon-battery-75:before { content: '\e807'; } /* '' */
|
.icon-battery-75:before { content: '\e807'; } /* '' */
|
||||||
.icon-battery-100:before { content: '\e808'; } /* '' */
|
.icon-battery-100:before { content: '\e808'; } /* '' */
|
||||||
.icon-cancel-circled:before { content: '\e809'; } /* '' */
|
.icon-cancel-circled:before { content: '\e809'; } /* '' */
|
||||||
.icon-volume:before { content: '\e80a'; } /* '' */
|
.icon-volume:before { content: '\e80a'; } /* '' */
|
||||||
|
.icon-plus:before { content: '\e80b'; } /* '' */
|
||||||
@@ -270,6 +270,7 @@ body {
|
|||||||
<div title="Code: 0xe808" class="the-icons span3"><i class="icon-battery-100"></i> <span class="i-name">icon-battery-100</span><span class="i-code">0xe808</span></div>
|
<div title="Code: 0xe808" class="the-icons span3"><i class="icon-battery-100"></i> <span class="i-name">icon-battery-100</span><span class="i-code">0xe808</span></div>
|
||||||
<div title="Code: 0xe809" class="the-icons span3"><i class="icon-cancel-circled"></i> <span class="i-name">icon-cancel-circled</span><span class="i-code">0xe809</span></div>
|
<div title="Code: 0xe809" class="the-icons span3"><i class="icon-cancel-circled"></i> <span class="i-name">icon-cancel-circled</span><span class="i-code">0xe809</span></div>
|
||||||
<div title="Code: 0xe80a" class="the-icons span3"><i class="icon-volume"></i> <span class="i-name">icon-volume</span><span class="i-code">0xe80a</span></div>
|
<div title="Code: 0xe80a" class="the-icons span3"><i class="icon-volume"></i> <span class="i-name">icon-volume</span><span class="i-code">0xe80a</span></div>
|
||||||
|
<div title="Code: 0xe80b" class="the-icons span3"><i class="icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe80b</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
|
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
|
||||||
|
|||||||
Binary file not shown.
@@ -17,6 +17,7 @@
|
|||||||
<glyph glyph-name="battery-100" unicode="" d="m365 194q-21 0-37 15t-15 37l0 209q0 21 15 36t37 15 36-15 15-36l0-209q0-21-15-37t-36-15z m-156 0q-21 0-37 15t-16 37l0 209q0 21 16 36t37 15 36-15 15-36l0-209q0-21-15-37t-36-15z m469 0q-22 0-37 15t-16 37l0 209q0 21 16 36t37 15 36-15 15-36l0-209q0-21-15-37t-36-15z m-157 0q-21 0-37 15t-15 37l0 209q0 21 15 36t37 15 37-15 15-36l0-209q0-21-15-37t-37-15z m364 312q44 0 74-30t31-73l0-104q0-44-31-75t-74-30q0-65-46-111t-110-45l-573 0q-65 0-110 45t-46 111l0 312q0 65 46 111t110 46l573 0q65 0 110-46t46-111z m-104-312l0 312q0 22-15 37t-37 16l-573 0q-21 0-37-16t-15-37l0-312q0-21 15-36t37-15l573 0q21 0 37 15t15 36z" horiz-adv-x="990" />
|
<glyph glyph-name="battery-100" unicode="" d="m365 194q-21 0-37 15t-15 37l0 209q0 21 15 36t37 15 36-15 15-36l0-209q0-21-15-37t-36-15z m-156 0q-21 0-37 15t-16 37l0 209q0 21 16 36t37 15 36-15 15-36l0-209q0-21-15-37t-36-15z m469 0q-22 0-37 15t-16 37l0 209q0 21 16 36t37 15 36-15 15-36l0-209q0-21-15-37t-36-15z m-157 0q-21 0-37 15t-15 37l0 209q0 21 15 36t37 15 37-15 15-36l0-209q0-21-15-37t-37-15z m364 312q44 0 74-30t31-73l0-104q0-44-31-75t-74-30q0-65-46-111t-110-45l-573 0q-65 0-110 45t-46 111l0 312q0 65 46 111t110 46l573 0q65 0 110-46t46-111z m-104-312l0 312q0 22-15 37t-37 16l-573 0q-21 0-37-16t-15-37l0-312q0-21 15-36t37-15l573 0q21 0 37 15t15 36z" horiz-adv-x="990" />
|
||||||
<glyph glyph-name="cancel-circled" unicode="" d="m420 770q174 0 297-123t123-297-123-297-297-123-297 123-123 297 123 297 297 123z m86-420l154 154-86 86-154-152-152 152-88-86 154-154-154-152 88-86 152 152 154-152 86 86z" horiz-adv-x="840" />
|
<glyph glyph-name="cancel-circled" unicode="" d="m420 770q174 0 297-123t123-297-123-297-297-123-297 123-123 297 123 297 297 123z m86-420l154 154-86 86-154-152-152 152-88-86 154-154-154-152 88-86 152 152 154-152 86 86z" horiz-adv-x="840" />
|
||||||
<glyph glyph-name="volume" unicode="" d="m0 142l0 416 236 0 354 289 0-994-354 289-236 0z m652 35q73 74 73 176t-73 178l71 74q105-106 107-254 0-145-107-246z m118-119q123 119 123 295t-123 299l76 74q154-154 154-372t-154-372z" horiz-adv-x="1000" />
|
<glyph glyph-name="volume" unicode="" d="m0 142l0 416 236 0 354 289 0-994-354 289-236 0z m652 35q73 74 73 176t-73 178l71 74q105-106 107-254 0-145-107-246z m118-119q123 119 123 295t-123 299l76 74q154-154 154-372t-154-372z" horiz-adv-x="1000" />
|
||||||
|
<glyph glyph-name="plus" unicode="" d="m0 209l0 282 359 0 0 359 282 0 0-359 359 0 0-282-359 0 0-359-282 0 0 359-359 0z" horiz-adv-x="1000" />
|
||||||
</font>
|
</font>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
+60
-1
@@ -1,7 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, maximum-scale=1, initial-scale=1, user-scalable=0" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<link rel="apple-touch-icon" href="/images/logomobile.png">
|
||||||
<title>NightScout</title>
|
<title>NightScout</title>
|
||||||
<link href="/images/round1.png" rel="icon" id="favicon" type="image/png" />
|
<link href="/images/round1.png" rel="icon" id="favicon" type="image/png" />
|
||||||
<link rel="stylesheet" type="text/css" href="/css/main.css?v=0.3.3" />
|
<link rel="stylesheet" type="text/css" href="/css/main.css?v=0.3.3" />
|
||||||
@@ -16,6 +18,7 @@
|
|||||||
<a id="testAlarms" class="tip" original-title="Alarm Test / Smartphone Enable" href="#"><i class="icon-volume"></i></a>
|
<a id="testAlarms" class="tip" original-title="Alarm Test / Smartphone Enable" href="#"><i class="icon-volume"></i></a>
|
||||||
<a id="hideToolbar" class="tip" original-title="Hides the toolbar" href="#"><i class="icon-angle-double-up"></i></a>
|
<a id="hideToolbar" class="tip" original-title="Hides the toolbar" href="#"><i class="icon-angle-double-up"></i></a>
|
||||||
<a id="drawerToggle" class="tip" original-title="Settings" href="#"><i class="icon-cog"></i></a>
|
<a id="drawerToggle" class="tip" original-title="Settings" href="#"><i class="icon-cog"></i></a>
|
||||||
|
<a id="treatmentDrawerToggle" class="tip" original-title="Treatments" href="#"><i class="icon-plus"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="customTitle">Nightscout</h1>
|
<h1 class="customTitle">Nightscout</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,6 +71,11 @@
|
|||||||
<dd><input type="radio" name="units-browser" id="mgdl-browser" value="mg/dl" checked /><label for="mgdl-browser">mg/dL</label><br />
|
<dd><input type="radio" name="units-browser" id="mgdl-browser" value="mg/dl" checked /><label for="mgdl-browser">mg/dL</label><br />
|
||||||
<dd><input type="radio" name="units-browser" id="mmol-browser" value="mmol" /><label for="mmol-browser">mmol/L</label>
|
<dd><input type="radio" name="units-browser" id="mmol-browser" value="mmol" /><label for="mmol-browser">mmol/L</label>
|
||||||
</dl>
|
</dl>
|
||||||
|
<dl class="toggle">
|
||||||
|
<dt>Enable Alarms <a class="tip" original-title="When enabled the an alarm will sound."><i class="icon-help-circled"></i></a></dt>
|
||||||
|
<dd><input type="checkbox" name="alarmhigh-browser" id="alarmhigh-browser" /><label for="alarmhigh-browser">High Alarm</label></dd>
|
||||||
|
<dd><input type="checkbox" name="alarmlow-browser" id="alarmlow-browser" /><label for="alarmlow-browser">Low Alarm</label></dd>
|
||||||
|
</dl>
|
||||||
<dl class="toggle">
|
<dl class="toggle">
|
||||||
<dt>Night Mode <a class="tip" original-title="When enabled the page will be dimmed from 10pm - 6am."><i class="icon-help-circled"></i></a></dt>
|
<dt>Night Mode <a class="tip" original-title="When enabled the page will be dimmed from 10pm - 6am."><i class="icon-help-circled"></i></a></dt>
|
||||||
<dd><input type="checkbox" name="nightmode-browser" id="nightmode-browser" /><label for="nightmode-browser">Enable</label></dd>
|
<dd><input type="checkbox" name="nightmode-browser" id="nightmode-browser" /><label for="nightmode-browser">Enable</label></dd>
|
||||||
@@ -76,6 +84,11 @@
|
|||||||
<dt>Custom Title</dt>
|
<dt>Custom Title</dt>
|
||||||
<dd><input type="text" id="customTitle" value="Nightscout" /></dd>
|
<dd><input type="text" id="customTitle" value="Nightscout" /></dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
<dl class="radio">
|
||||||
|
<dt>Theme</dt>
|
||||||
|
<dd><input type="radio" name="theme-browser" id="theme-default-browser" value="default" checked /><label for="theme-default-browser">Default</label><br />
|
||||||
|
<dd><input type="radio" name="theme-browser" id="theme-colors-browser" value="colors" /><label for="theme-colors-browser">Colors</label>
|
||||||
|
</dl>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<input type="submit" id="save" value="Save" />
|
<input type="submit" id="save" value="Save" />
|
||||||
@@ -102,6 +115,52 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="treatmentDrawer">
|
||||||
|
<form id="treatment-form" onsubmit="return treatmentSubmit();">
|
||||||
|
<fieldset class="treatmentData">
|
||||||
|
<legend>Log a Treatment</legend>
|
||||||
|
<dl>
|
||||||
|
<dt>Entered By:</dt>
|
||||||
|
<dd><input type="text" id="enteredBy" value="" /></dd>
|
||||||
|
</dl>
|
||||||
|
<label for="eventType">Event Type:</label>
|
||||||
|
<select id="eventType">
|
||||||
|
<option value="BG Check">Blood Glucose Check</option>
|
||||||
|
<option value="Snack Bolus">Snack Bolus</option>
|
||||||
|
<option value="Meal Bolus">Meal Bolus</option>
|
||||||
|
<option value="Correction Bolus">Correction Bolus</option>
|
||||||
|
<option value="Food Correction">Food Correction</option>
|
||||||
|
<option value="Question">Note or Question to Parents</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Glucose Reading</legend>
|
||||||
|
<input type="number" step="any" id="glucoseValue" />
|
||||||
|
<label><br>Measurement Method:<br></label>
|
||||||
|
<label for="meter">
|
||||||
|
<input type="radio" name="glucoseType" id="meter" value="Finger"/>
|
||||||
|
<span>Meter</span>
|
||||||
|
</label>
|
||||||
|
<label for="sensor">
|
||||||
|
<input type="radio" name="glucoseType" id="sensor" value="Sensor"/>
|
||||||
|
<span>Sensor<br></span>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<label for="carbsGiven">Carbs Given (mass in grams):<br></label>
|
||||||
|
<input type="number" step="any" min="0" id="carbsGiven" />
|
||||||
|
<br>
|
||||||
|
<label for="insulinGiven"><br>Insulin Given (units):<br></label>
|
||||||
|
<input type="number" step="any" min="0" id="insulinGiven" />
|
||||||
|
<br>
|
||||||
|
<!-- Label and textarea -->
|
||||||
|
<label for="notes"><br>Additional Notes, Comments:<br></label>
|
||||||
|
<textarea id="notes"></textarea><br>
|
||||||
|
<button type="submit">Submit Form</button>
|
||||||
|
</fieldset>
|
||||||
|
<a href="/treatments.html" target="treatments">View all treatments</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="audio alarms">
|
<div class="audio alarms">
|
||||||
<audio src="/audio/alarm.mp3" preload="auto" loop="true" class="alarm mp3" type="audio/mp3"></audio>
|
<audio src="/audio/alarm.mp3" preload="auto" loop="true" class="alarm mp3" type="audio/mp3"></audio>
|
||||||
<audio src="/audio/alarm2.mp3" preload="auto" loop="true" class="urgent alarm2 mp3" type="audio/mp3"></audio>
|
<audio src="/audio/alarm2.mp3" preload="auto" loop="true" class="urgent alarm2 mp3" type="audio/mp3"></audio>
|
||||||
|
|||||||
+241
-122
@@ -1,8 +1,8 @@
|
|||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var retrospectivePredictor = true,
|
var latestSGV,
|
||||||
latestSGV,
|
errorCode,
|
||||||
treatments,
|
treatments,
|
||||||
padding = { top: 20, right: 10, bottom: 30, left: 10 },
|
padding = { top: 20, right: 10, bottom: 30, left: 10 },
|
||||||
opacity = {current: 1, DAY: 1, NIGHT: 0.5},
|
opacity = {current: 1, DAY: 1, NIGHT: 0.5},
|
||||||
@@ -23,7 +23,9 @@
|
|||||||
clip,
|
clip,
|
||||||
TWENTY_FIVE_MINS_IN_MS = 1500000,
|
TWENTY_FIVE_MINS_IN_MS = 1500000,
|
||||||
THIRTY_MINS_IN_MS = 1800000,
|
THIRTY_MINS_IN_MS = 1800000,
|
||||||
|
FORTY_MINS_IN_MS = 2400000,
|
||||||
FORTY_TWO_MINS_IN_MS = 2520000,
|
FORTY_TWO_MINS_IN_MS = 2520000,
|
||||||
|
SIXTY_MINS_IN_MS = 3600000,
|
||||||
FOCUS_DATA_RANGE_MS = 12600000, // 3.5 hours of actual data
|
FOCUS_DATA_RANGE_MS = 12600000, // 3.5 hours of actual data
|
||||||
FORMAT_TIME = '%I:%M%', //alternate format '%H:%M'
|
FORMAT_TIME = '%I:%M%', //alternate format '%H:%M'
|
||||||
audio = document.getElementById('audio'),
|
audio = document.getElementById('audio'),
|
||||||
@@ -41,6 +43,14 @@
|
|||||||
var tickValues = [2.0, 3.0, 4.0, 6.0, 10.0, 15.0, 22.0];
|
var tickValues = [2.0, 3.0, 4.0, 6.0, 10.0, 15.0, 22.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: get these from the config
|
||||||
|
var targetTop = 180,
|
||||||
|
targetBottom = 80;
|
||||||
|
|
||||||
|
var futureOpacity = d3.scale.linear( )
|
||||||
|
.domain([TWENTY_FIVE_MINS_IN_MS, SIXTY_MINS_IN_MS])
|
||||||
|
.range([0.8, 0.1]);
|
||||||
|
|
||||||
// create svg and g to contain the chart contents
|
// create svg and g to contain the chart contents
|
||||||
var charts = d3.select('#chartContainer').append('svg')
|
var charts = d3.select('#chartContainer').append('svg')
|
||||||
.append('g')
|
.append('g')
|
||||||
@@ -135,7 +145,7 @@
|
|||||||
// get the desired opacity for context chart based on the brush extent
|
// get the desired opacity for context chart based on the brush extent
|
||||||
function highlightBrushPoints(data) {
|
function highlightBrushPoints(data) {
|
||||||
if (data.date.getTime() >= brush.extent()[0].getTime() && data.date.getTime() <= brush.extent()[1].getTime()) {
|
if (data.date.getTime() >= brush.extent()[0].getTime() && data.date.getTime() <= brush.extent()[1].getTime()) {
|
||||||
return 1;
|
return futureOpacity(data.date - latestSGV.x);
|
||||||
} else {
|
} else {
|
||||||
return 0.5;
|
return 0.5;
|
||||||
}
|
}
|
||||||
@@ -211,15 +221,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var element = document.getElementById('bgButton').hidden == '';
|
var element = document.getElementById('bgButton').hidden == '';
|
||||||
|
|
||||||
var nowDate = new Date(brushExtent[1] - THIRTY_MINS_IN_MS);
|
var nowDate = new Date(brushExtent[1] - THIRTY_MINS_IN_MS);
|
||||||
|
|
||||||
// predict for retrospective data
|
// predict for retrospective data
|
||||||
if (retrospectivePredictor && brushExtent[1].getTime() - THIRTY_MINS_IN_MS < now && element != true) {
|
if (brushExtent[1].getTime() - THIRTY_MINS_IN_MS < now && element != true) {
|
||||||
// filter data for -12 and +5 minutes from reference time for retrospective focus data prediction
|
// filter data for -12 and +5 minutes from reference time for retrospective focus data prediction
|
||||||
var nowData = data.filter(function(d) {
|
var nowData = data.filter(function(d) {
|
||||||
return d.date.getTime() >= brushExtent[1].getTime() - FORTY_TWO_MINS_IN_MS &&
|
return d.date.getTime() >= brushExtent[1].getTime() - FORTY_TWO_MINS_IN_MS &&
|
||||||
d.date.getTime() <= brushExtent[1].getTime() - TWENTY_FIVE_MINS_IN_MS
|
d.date.getTime() <= brushExtent[1].getTime() - TWENTY_FIVE_MINS_IN_MS &&
|
||||||
|
d.color != 'none';
|
||||||
});
|
});
|
||||||
if (nowData.length > 1) {
|
if (nowData.length > 1) {
|
||||||
var prediction = predictAR(nowData);
|
var prediction = predictAR(nowData);
|
||||||
@@ -238,18 +248,69 @@
|
|||||||
$('#currentTime')
|
$('#currentTime')
|
||||||
.text(formatTime(new Date(brushExtent[1] - THIRTY_MINS_IN_MS)))
|
.text(formatTime(new Date(brushExtent[1] - THIRTY_MINS_IN_MS)))
|
||||||
.css('text-decoration','line-through');
|
.css('text-decoration','line-through');
|
||||||
} else if (retrospectivePredictor) {
|
|
||||||
|
$('#lastEntry').text("RETRO").removeClass('current');
|
||||||
|
|
||||||
|
$('.container #noButton .currentBG').css({color: 'grey'});
|
||||||
|
$('.container #noButton .currentDirection').css({color: 'grey'});
|
||||||
|
|
||||||
|
} else {
|
||||||
// if the brush comes back into the current time range then it should reset to the current time and sg
|
// if the brush comes back into the current time range then it should reset to the current time and sg
|
||||||
|
var nowData = data.filter(function(d) {
|
||||||
|
return d.color != 'none';
|
||||||
|
});
|
||||||
|
nowData = [nowData[nowData.length - 2], nowData[nowData.length - 1]];
|
||||||
|
var prediction = predictAR(nowData);
|
||||||
|
focusData = focusData.concat(prediction);
|
||||||
var dateTime = new Date(now);
|
var dateTime = new Date(now);
|
||||||
nowDate = dateTime;
|
nowDate = dateTime;
|
||||||
$('#currentTime')
|
$('#currentTime')
|
||||||
.text(formatTime(dateTime))
|
.text(formatTime(dateTime))
|
||||||
.css('text-decoration','');
|
.css('text-decoration', '');
|
||||||
$('.container .currentBG')
|
|
||||||
.text(scaleBg(latestSGV.y))
|
if (errorCode) {
|
||||||
.css('text-decoration','');
|
var errorDisplay;
|
||||||
$('.container .currentDirection')
|
|
||||||
.html(latestSGV.direction);
|
switch (parseInt(errorCode)) {
|
||||||
|
case 0: errorDisplay = '??0'; break; //None
|
||||||
|
case 1: errorDisplay = '?SN'; break; //SENSOR_NOT_ACTIVE
|
||||||
|
case 2: errorDisplay = '??2'; break; //MINIMAL_DEVIATION
|
||||||
|
case 3: errorDisplay = '?NA'; break; //NO_ANTENNA
|
||||||
|
case 5: errorDisplay = '?NC'; break; //SENSOR_NOT_CALIBRATED
|
||||||
|
case 6: errorDisplay = '?CD'; break; //COUNTS_DEVIATION
|
||||||
|
case 7: errorDisplay = '??7'; break; //?
|
||||||
|
case 8: errorDisplay = '??8'; break; //?
|
||||||
|
case 9: errorDisplay = '⌛'; break; //ABSOLUTE_DEVIATION
|
||||||
|
case 10: errorDisplay = '???'; break; //POWER_DEVIATION
|
||||||
|
case 12: errorDisplay = '?RF'; break; //BAD_RF
|
||||||
|
default: errorDisplay = '?' + parseInt(errorCode) + '?'; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#lastEntry').text("CGM ERROR").removeClass('current').addClass("urgent");
|
||||||
|
|
||||||
|
$('.container .currentBG').html(errorDisplay)
|
||||||
|
.css('text-decoration', '');
|
||||||
|
$('.container .currentDirection').html('✖');
|
||||||
|
|
||||||
|
var color = sgvToColor(errorCode);
|
||||||
|
$('.container #noButton .currentBG').css({color: color});
|
||||||
|
$('.container #noButton .currentDirection').css({color: color});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var secsSinceLast = (Date.now() - new Date(latestSGV.x).getTime()) / 1000;
|
||||||
|
$('#lastEntry').text(timeAgo(secsSinceLast)).toggleClass('current', secsSinceLast < 10 * 60);
|
||||||
|
|
||||||
|
$('.container .currentBG')
|
||||||
|
.text(scaleBg(latestSGV.y))
|
||||||
|
.css('text-decoration', '');
|
||||||
|
$('.container .currentDirection')
|
||||||
|
.html(latestSGV.direction);
|
||||||
|
|
||||||
|
var color = sgvToColor(latestSGV.y);
|
||||||
|
$('.container #noButton .currentBG').css({color: color});
|
||||||
|
$('.container #noButton .currentDirection').css({color: color});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xScale.domain(brush.extent());
|
xScale.domain(brush.extent());
|
||||||
@@ -271,6 +332,7 @@
|
|||||||
.attr('cx', function (d) { return xScale(d.date); })
|
.attr('cx', function (d) { return xScale(d.date); })
|
||||||
.attr('cy', function (d) { return yScale(d.sgv); })
|
.attr('cy', function (d) { return yScale(d.sgv); })
|
||||||
.attr('fill', function (d) { return d.color; })
|
.attr('fill', function (d) { return d.color; })
|
||||||
|
.attr('opacity', function (d) { return futureOpacity(d.date - latestSGV.x); })
|
||||||
.attr('r', 3);
|
.attr('r', 3);
|
||||||
|
|
||||||
focusCircles.exit()
|
focusCircles.exit()
|
||||||
@@ -556,18 +618,18 @@
|
|||||||
.transition()
|
.transition()
|
||||||
.duration(UPDATE_TRANS_MS)
|
.duration(UPDATE_TRANS_MS)
|
||||||
.attr('x1', xScale2(dataRange[0]))
|
.attr('x1', xScale2(dataRange[0]))
|
||||||
.attr('y1', yScale2(scaleBg(180)))
|
.attr('y1', yScale2(scaleBg(targetTop)))
|
||||||
.attr('x2', xScale2(dataRange[1]))
|
.attr('x2', xScale2(dataRange[1]))
|
||||||
.attr('y2', yScale2(scaleBg(180)));
|
.attr('y2', yScale2(scaleBg(targetTop)));
|
||||||
|
|
||||||
// transition low line to correct location
|
// transition low line to correct location
|
||||||
context.select('.low-line')
|
context.select('.low-line')
|
||||||
.transition()
|
.transition()
|
||||||
.duration(UPDATE_TRANS_MS)
|
.duration(UPDATE_TRANS_MS)
|
||||||
.attr('x1', xScale2(dataRange[0]))
|
.attr('x1', xScale2(dataRange[0]))
|
||||||
.attr('y1', yScale2(scaleBg(80)))
|
.attr('y1', yScale2(scaleBg(targetBottom)))
|
||||||
.attr('x2', xScale2(dataRange[1]))
|
.attr('x2', xScale2(dataRange[1]))
|
||||||
.attr('y2', yScale2(scaleBg(80)));
|
.attr('y2', yScale2(scaleBg(targetBottom)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,7 +715,6 @@
|
|||||||
socket.on('now', function (d) {
|
socket.on('now', function (d) {
|
||||||
now = d;
|
now = d;
|
||||||
var dateTime = new Date(now);
|
var dateTime = new Date(now);
|
||||||
// lixgbg old: $('#currentTime').text(d3.time.format('%I:%M%p')(dateTime));
|
|
||||||
$('#currentTime').text(formatTime(dateTime));
|
$('#currentTime').text(formatTime(dateTime));
|
||||||
|
|
||||||
// Dim the screen by reducing the opacity when at nighttime
|
// Dim the screen by reducing the opacity when at nighttime
|
||||||
@@ -668,40 +729,35 @@
|
|||||||
|
|
||||||
socket.on('sgv', function (d) {
|
socket.on('sgv', function (d) {
|
||||||
if (d.length > 1) {
|
if (d.length > 1) {
|
||||||
|
errorCode = d.length >= 5 ? d[4] : undefined;
|
||||||
|
|
||||||
// change the next line so that it uses the prediction if the signal gets lost (max 1/2 hr)
|
// change the next line so that it uses the prediction if the signal gets lost (max 1/2 hr)
|
||||||
if (d[0].length) {
|
if (d[0].length) {
|
||||||
var current = d[0][d[0].length - 1];
|
latestSGV = d[0][d[0].length - 1];
|
||||||
latestSGV = current;
|
|
||||||
var secsSinceLast = (Date.now() - new Date(current.x).getTime()) / 1000;
|
|
||||||
var currentBG = current.y;
|
|
||||||
|
|
||||||
//TODO: currently these are filtered on the server
|
//TODO: alarmHigh/alarmLow probably shouldn't be here
|
||||||
//TODO: use icons for these magic values
|
if (browserSettings.alarmHigh) {
|
||||||
switch (current.y) {
|
$('.container .current').toggleClass('high', latestSGV.y > 180);
|
||||||
case 0: currentBG = '??0'; break; //None
|
}
|
||||||
case 1: currentBG = '?SN'; break; //SENSOR_NOT_ACTIVE
|
if (browserSettings.alarmLow) {
|
||||||
case 2: currentBG = '??2'; break; //MINIMAL_DEVIATION
|
$('.container .current').toggleClass('low', latestSGV.y < 70);
|
||||||
case 3: currentBG = '?NA'; break; //NO_ANTENNA
|
|
||||||
case 5: currentBG = '?NC'; break; //SENSOR_NOT_CALIBRATED
|
|
||||||
case 6: currentBG = '?CD'; break; //COUNTS_DEVIATION
|
|
||||||
case 7: currentBG = '??7'; break; //?
|
|
||||||
case 8: currentBG = '??8'; break; //?
|
|
||||||
case 9: currentBG = '?AD'; break; //ABSOLUTE_DEVIATION
|
|
||||||
case 10: currentBG = '?PD'; break; //POWER_DEVIATION
|
|
||||||
case 12: currentBG = '?RF'; break; //BAD_RF
|
|
||||||
default:
|
|
||||||
currentBG = scaleBg(currentBG);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#lastEntry').text(timeAgo(secsSinceLast)).toggleClass('current', secsSinceLast < 10 * 60);
|
|
||||||
$('.container .currentBG').text(currentBG);
|
|
||||||
$('.container .currentDirection').html(current.direction);
|
|
||||||
$('.container .current').toggleClass('high', current.y > 180).toggleClass('low', current.y < 70)
|
|
||||||
}
|
}
|
||||||
data = d[0].map(function (obj) { return { date: new Date(obj.x), sgv: scaleBg(obj.y), direction: obj.direction, color: 'grey'} });
|
data = d[0].map(function (obj) {
|
||||||
data = data.concat(d[1].map(function (obj) { return { date: new Date(obj.x), sgv: scaleBg(obj.y), color: 'blue'} }));
|
return { date: new Date(obj.x), sgv: scaleBg(obj.y), direction: obj.direction, color: sgvToColor(obj.y)}
|
||||||
|
});
|
||||||
|
// TODO: This is a kludge to advance the time as data becomes stale by making old predictor clear (using color = 'none')
|
||||||
|
// This shouldn't have to be sent and can be fixed by using xScale.domain([x0,x1]) function with
|
||||||
|
// 2 days before now as x0 and 30 minutes from now for x1 for context plot, but this will be
|
||||||
|
// required to happen when "now" event is sent from websocket.js every minute. When fixed,
|
||||||
|
// remove all "color != 'none'" code
|
||||||
|
data = data.concat(d[1].map(function (obj) { return { date: new Date(obj.x), sgv: scaleBg(obj.y), color: 'none'} }));
|
||||||
data = data.concat(d[2].map(function (obj) { return { date: new Date(obj.x), sgv: scaleBg(obj.y), color: 'red'} }));
|
data = data.concat(d[2].map(function (obj) { return { date: new Date(obj.x), sgv: scaleBg(obj.y), color: 'red'} }));
|
||||||
|
|
||||||
|
data.forEach(function (d) {
|
||||||
|
if (d.sgv < 39)
|
||||||
|
d.color = "transparent";
|
||||||
|
})
|
||||||
|
|
||||||
treatments = d[3];
|
treatments = d[3];
|
||||||
if (!isInitialData) {
|
if (!isInitialData) {
|
||||||
@@ -713,6 +769,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function sgvToColor(sgv) {
|
||||||
|
var color = 'grey';
|
||||||
|
|
||||||
|
if (browserSettings.theme == "colors") {
|
||||||
|
if (sgv > targetTop) {
|
||||||
|
color = 'yellow';
|
||||||
|
} else if (sgv >= targetBottom && sgv <= targetTop) {
|
||||||
|
color = '#4cff00';
|
||||||
|
} else if (sgv < targetBottom) {
|
||||||
|
color = 'red';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -722,16 +794,20 @@
|
|||||||
console.log('Client connected to server.')
|
console.log('Client connected to server.')
|
||||||
});
|
});
|
||||||
socket.on('alarm', function () {
|
socket.on('alarm', function () {
|
||||||
console.log("Alarm raised!");
|
if (browserSettings.alarmHigh) {
|
||||||
currentAlarmType = 'alarm';
|
console.log("Alarm raised!");
|
||||||
generateAlarm(alarmSound);
|
currentAlarmType = 'alarm';
|
||||||
|
generateAlarm(alarmSound);
|
||||||
|
}
|
||||||
brushInProgress = false;
|
brushInProgress = false;
|
||||||
updateChart(false);
|
updateChart(false);
|
||||||
});
|
});
|
||||||
socket.on('urgent_alarm', function () {
|
socket.on('urgent_alarm', function () {
|
||||||
console.log("Urgent alarm raised!");
|
if (browserSettings.alarmLow) {
|
||||||
currentAlarmType = 'urgent_alarm';
|
console.log("Urgent alarm raised!");
|
||||||
generateAlarm(urgentAlarmSound);
|
currentAlarmType = 'urgent_alarm';
|
||||||
|
generateAlarm(urgentAlarmSound);
|
||||||
|
}
|
||||||
brushInProgress = false;
|
brushInProgress = false;
|
||||||
updateChart(false);
|
updateChart(false);
|
||||||
});
|
});
|
||||||
@@ -745,11 +821,11 @@
|
|||||||
|
|
||||||
$('#testAlarms').click(function(event) {
|
$('#testAlarms').click(function(event) {
|
||||||
d3.select('.audio.alarms audio').each(function (data, i) {
|
d3.select('.audio.alarms audio').each(function (data, i) {
|
||||||
var audio = this;
|
var audio = this;
|
||||||
playAlarm(audio);
|
playAlarm(audio);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
audio.pause();
|
audio.pause();
|
||||||
}, 4000);
|
}, 4000);
|
||||||
});
|
});
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
@@ -758,9 +834,9 @@
|
|||||||
alarmInProgress = true;
|
alarmInProgress = true;
|
||||||
var selector = '.audio.alarms audio.' + file;
|
var selector = '.audio.alarms audio.' + file;
|
||||||
d3.select(selector).each(function (d, i) {
|
d3.select(selector).each(function (d, i) {
|
||||||
var audio = this;
|
var audio = this;
|
||||||
playAlarm(audio);
|
playAlarm(audio);
|
||||||
$(this).addClass('playing');
|
$(this).addClass('playing');
|
||||||
});
|
});
|
||||||
var element = document.getElementById('bgButton');
|
var element = document.getElementById('bgButton');
|
||||||
element.hidden = '';
|
element.hidden = '';
|
||||||
@@ -789,9 +865,9 @@
|
|||||||
element = document.getElementById('noButton');
|
element = document.getElementById('noButton');
|
||||||
element.hidden = '';
|
element.hidden = '';
|
||||||
d3.select('audio.playing').each(function (d, i) {
|
d3.select('audio.playing').each(function (d, i) {
|
||||||
var audio = this;
|
var audio = this;
|
||||||
audio.pause();
|
audio.pause();
|
||||||
$(this).removeClass('playing');
|
$(this).removeClass('playing');
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".time").show();
|
$(".time").show();
|
||||||
@@ -839,9 +915,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parts.value)
|
if (parts.value)
|
||||||
return parts.value + ' ' + parts.label + ' ago';
|
return parts.value + ' ' + parts.label + ' ago';
|
||||||
else
|
else
|
||||||
return parts.label;
|
return parts.label;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -851,64 +927,91 @@
|
|||||||
//draw a compact visualization of a treatment (carbs, insulin)
|
//draw a compact visualization of a treatment (carbs, insulin)
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
function drawTreatment(treatment, scale, showValues) {
|
function drawTreatment(treatment, scale, showValues) {
|
||||||
var carbs = treatment.carbs;
|
|
||||||
var insulin = treatment.insulin;
|
|
||||||
var CR = treatment.CR;
|
|
||||||
|
|
||||||
var R1 = Math.sqrt(Math.min(carbs, insulin * CR)) / scale,
|
if (!treatment.CR) {
|
||||||
R2 = Math.sqrt(Math.max(carbs, insulin * CR)) / scale,
|
//plot a simple treatment point
|
||||||
R3 = R2 + 8 / scale;
|
console.info("plotting treatment", treatment);
|
||||||
|
var treatmentDots = focus.selectAll('treatment-dot')
|
||||||
|
.data(treatment)
|
||||||
|
.enter()
|
||||||
|
.append('g')
|
||||||
|
.attr('transform', 'translate(' + xScale(treatment.x) + ', ' + yScale(scaleBg(300)) + ')');
|
||||||
|
|
||||||
var arc_data = [
|
//TODO: some d3 magic to get the treatments to display and show a tooltip
|
||||||
{ 'element': '', 'color': '#9c4333', 'start': -1.5708, 'end': 1.5708, 'inner': 0, 'outer': R1 },
|
} else {
|
||||||
{ 'element': '', 'color': '#d4897b', 'start': -1.5708, 'end': 1.5708, 'inner': R1, 'outer': R2 },
|
var carbs = treatment.carbs;
|
||||||
{ 'element': '', 'color': 'transparent', 'start': -1.5708, 'end': 1.5708, 'inner': R2, 'outer': R3 },
|
var insulin = treatment.insulin;
|
||||||
{ 'element': '', 'color': '#3d53b7', 'start': 1.5708, 'end': 4.7124, 'inner': 0, 'outer': R1 },
|
var CR = treatment.CR;
|
||||||
{ 'element': '', 'color': '#5d72c9', 'start': 1.5708, 'end': 4.7124, 'inner': R1, 'outer': R2 },
|
|
||||||
{ 'element': '', 'color': 'transparent', 'start': 1.5708, 'end': 4.7124, 'inner': R2, 'outer': R3 }
|
|
||||||
];
|
|
||||||
|
|
||||||
if (carbs < insulin * CR) arc_data[1].color = 'transparent';
|
var R1 = Math.sqrt(Math.min(carbs, insulin * CR)) / scale,
|
||||||
if (carbs > insulin * CR) arc_data[4].color = 'transparent';
|
R2 = Math.sqrt(Math.max(carbs, insulin * CR)) / scale,
|
||||||
if (carbs > 0) arc_data[2].element = Math.round(carbs) + ' g';
|
R3 = R2 + 8 / scale;
|
||||||
if (insulin > 0) arc_data[5].element = Math.round(insulin * 10) / 10 + ' U';
|
|
||||||
|
|
||||||
var arc = d3.svg.arc()
|
var arc_data = [
|
||||||
.innerRadius(function (d) { return 5 * d.inner; })
|
{ 'element': '', 'color': '#9c4333', 'start': -1.5708, 'end': 1.5708, 'inner': 0, 'outer': R1 },
|
||||||
.outerRadius(function (d) { return 5 * d.outer; })
|
{ 'element': '', 'color': '#d4897b', 'start': -1.5708, 'end': 1.5708, 'inner': R1, 'outer': R2 },
|
||||||
.endAngle(function (d) { return d.start; })
|
{ 'element': '', 'color': 'transparent', 'start': -1.5708, 'end': 1.5708, 'inner': R2, 'outer': R3 },
|
||||||
.startAngle(function (d) { return d.end; });
|
{ 'element': '', 'color': '#3d53b7', 'start': 1.5708, 'end': 4.7124, 'inner': 0, 'outer': R1 },
|
||||||
|
{ 'element': '', 'color': '#5d72c9', 'start': 1.5708, 'end': 4.7124, 'inner': R1, 'outer': R2 },
|
||||||
|
{ 'element': '', 'color': 'transparent', 'start': 1.5708, 'end': 4.7124, 'inner': R2, 'outer': R3 }
|
||||||
|
];
|
||||||
|
|
||||||
var treatmentDots = focus.selectAll('treatment-dot')
|
if (carbs < insulin * CR) arc_data[1].color = 'transparent';
|
||||||
.data(arc_data)
|
if (carbs > insulin * CR) arc_data[4].color = 'transparent';
|
||||||
.enter()
|
if (carbs > 0) arc_data[2].element = Math.round(carbs) + ' g';
|
||||||
.append('g')
|
if (insulin > 0) arc_data[5].element = Math.round(insulin * 10) / 10 + ' U';
|
||||||
.attr('transform', 'translate(' + xScale(treatment.x) + ', ' + yScale(scaleBg(treatment.y)) + ')');
|
|
||||||
|
|
||||||
var arcs = treatmentDots.append('path')
|
var arc = d3.svg.arc()
|
||||||
.attr('class', 'path')
|
.innerRadius(function (d) {
|
||||||
.attr('fill', function (d, i) { return d.color; })
|
return 5 * d.inner;
|
||||||
.attr('id', function (d, i) { return 's' + i; })
|
|
||||||
.attr('d', arc);
|
|
||||||
|
|
||||||
|
|
||||||
// labels for carbs and insulin
|
|
||||||
if (showValues) {
|
|
||||||
var label = treatmentDots.append('g')
|
|
||||||
.attr('class', 'path')
|
|
||||||
.attr('id', 'label')
|
|
||||||
.style('fill', 'white');
|
|
||||||
label.append('text')
|
|
||||||
.style('font-size', 30 / scale)
|
|
||||||
.style('font-family', 'Arial')
|
|
||||||
.attr('text-anchor', 'middle')
|
|
||||||
.attr('dy', '.35em')
|
|
||||||
.attr('transform', function (d) {
|
|
||||||
d.outerRadius = d.outerRadius * 2.1;
|
|
||||||
d.innerRadius = d.outerRadius * 2.1;
|
|
||||||
return 'translate(' + arc.centroid(d) + ')';
|
|
||||||
})
|
})
|
||||||
.text(function (d) { return d.element; })
|
.outerRadius(function (d) {
|
||||||
|
return 5 * d.outer;
|
||||||
|
})
|
||||||
|
.endAngle(function (d) {
|
||||||
|
return d.start;
|
||||||
|
})
|
||||||
|
.startAngle(function (d) {
|
||||||
|
return d.end;
|
||||||
|
});
|
||||||
|
|
||||||
|
var treatmentDots = focus.selectAll('treatment-dot')
|
||||||
|
.data(arc_data)
|
||||||
|
.enter()
|
||||||
|
.append('g')
|
||||||
|
.attr('transform', 'translate(' + xScale(treatment.x) + ', ' + yScale(scaleBg(treatment.y)) + ')');
|
||||||
|
|
||||||
|
var arcs = treatmentDots.append('path')
|
||||||
|
.attr('class', 'path')
|
||||||
|
.attr('fill', function (d, i) {
|
||||||
|
return d.color;
|
||||||
|
})
|
||||||
|
.attr('id', function (d, i) {
|
||||||
|
return 's' + i;
|
||||||
|
})
|
||||||
|
.attr('d', arc);
|
||||||
|
|
||||||
|
|
||||||
|
// labels for carbs and insulin
|
||||||
|
if (showValues) {
|
||||||
|
var label = treatmentDots.append('g')
|
||||||
|
.attr('class', 'path')
|
||||||
|
.attr('id', 'label')
|
||||||
|
.style('fill', 'white');
|
||||||
|
label.append('text')
|
||||||
|
.style('font-size', 30 / scale)
|
||||||
|
.style('font-family', 'Arial')
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.attr('dy', '.35em')
|
||||||
|
.attr('transform', function (d) {
|
||||||
|
d.outerRadius = d.outerRadius * 2.1;
|
||||||
|
d.innerRadius = d.outerRadius * 2.1;
|
||||||
|
return 'translate(' + arc.centroid(d) + ')';
|
||||||
|
})
|
||||||
|
.text(function (d) {
|
||||||
|
return d.element;
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,6 +1025,8 @@
|
|||||||
var BG_REF = 140;
|
var BG_REF = 140;
|
||||||
var BG_MIN = 36;
|
var BG_MIN = 36;
|
||||||
var BG_MAX = 400;
|
var BG_MAX = 400;
|
||||||
|
// these are the one sigma limits for the first 13 prediction interval uncertainties (65 minutes)
|
||||||
|
var CONE = [0.020, 0.041, 0.061, 0.081, 0.099, 0.116, 0.132, 0.146, 0.159, 0.171, 0.182, 0.192, 0.201];
|
||||||
if (actual.length < 2) {
|
if (actual.length < 2) {
|
||||||
var y = [Math.log(actual[0].sgv / BG_REF), Math.log(actual[0].sgv / BG_REF)];
|
var y = [Math.log(actual[0].sgv / BG_REF), Math.log(actual[0].sgv / BG_REF)];
|
||||||
} else {
|
} else {
|
||||||
@@ -932,17 +1037,31 @@
|
|||||||
y = [Math.log(actual[0].sgv / BG_REF), Math.log(actual[0].sgv / BG_REF)];
|
y = [Math.log(actual[0].sgv / BG_REF), Math.log(actual[0].sgv / BG_REF)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var n = 20;
|
|
||||||
var AR = [-0.723, 1.716];
|
var AR = [-0.723, 1.716];
|
||||||
var dt = actual[1].date.getTime();
|
var dt = actual[1].date.getTime();
|
||||||
for (var i = 0; i <= n; i++) {
|
var predictedColor = 'blue';
|
||||||
|
if (browserSettings.theme == "colors") {
|
||||||
|
predictedColor = 'cyan';
|
||||||
|
}
|
||||||
|
for (var i = 0; i < CONE.length; i++) {
|
||||||
y = [y[1], AR[0] * y[0] + AR[1] * y[1]];
|
y = [y[1], AR[0] * y[0] + AR[1] * y[1]];
|
||||||
dt = dt + FIVE_MINUTES;
|
dt = dt + FIVE_MINUTES;
|
||||||
predicted[i] = {
|
// Add 2000 ms so not same point as SG
|
||||||
date: new Date(dt+3000),
|
predicted[i * 2] = {
|
||||||
sgv: Math.max(BG_MIN, Math.min(BG_MAX, Math.round(BG_REF * Math.exp(y[1])))),
|
date: new Date(dt + 2000),
|
||||||
color: 'blue'
|
sgv: Math.max(BG_MIN, Math.min(BG_MAX, Math.round(BG_REF * Math.exp((y[1] - 2 * CONE[i]))))),
|
||||||
|
color: predictedColor
|
||||||
};
|
};
|
||||||
|
// Add 4000 ms so not same point as SG
|
||||||
|
predicted[i * 2 + 1] = {
|
||||||
|
date: new Date(dt + 4000),
|
||||||
|
sgv: Math.max(BG_MIN, Math.min(BG_MAX, Math.round(BG_REF * Math.exp((y[1] + 2 * CONE[i]))))),
|
||||||
|
color: predictedColor
|
||||||
|
};
|
||||||
|
predicted.forEach(function (d) {
|
||||||
|
if (d.sgv < BG_MIN)
|
||||||
|
d.color = "transparent";
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return predicted;
|
return predicted;
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-8
@@ -1,9 +1,13 @@
|
|||||||
var drawerIsOpen = false;
|
var drawerIsOpen = false;
|
||||||
|
var treatmentDrawerIsOpen = false;
|
||||||
var browserStorage = $.localStorage;
|
var browserStorage = $.localStorage;
|
||||||
var defaultSettings = {
|
var defaultSettings = {
|
||||||
"units": "mg/dl",
|
"units": "mg/dl",
|
||||||
"nightMode": false
|
"alarmHigh": true,
|
||||||
}
|
"alarmLow": true,
|
||||||
|
"nightMode": false,
|
||||||
|
"theme": "default"
|
||||||
|
};
|
||||||
|
|
||||||
var app = {};
|
var app = {};
|
||||||
$.ajax("/api/v1/status.json", {
|
$.ajax("/api/v1/status.json", {
|
||||||
@@ -26,29 +30,42 @@ $.ajax("/api/v1/status.json", {
|
|||||||
function getBrowserSettings(storage) {
|
function getBrowserSettings(storage) {
|
||||||
var json = {};
|
var json = {};
|
||||||
try {
|
try {
|
||||||
json = {
|
var json = {
|
||||||
"units": storage.get("units"),
|
"units": storage.get("units"),
|
||||||
|
"alarmHigh": storage.get("alarmHigh"),
|
||||||
|
"alarmLow": storage.get("alarmLow"),
|
||||||
"nightMode": storage.get("nightMode"),
|
"nightMode": storage.get("nightMode"),
|
||||||
"customTitle": storage.get("customTitle")
|
"customTitle": storage.get("customTitle"),
|
||||||
|
"theme": storage.get("theme")
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default browser units to server units if undefined.
|
// Default browser units to server units if undefined.
|
||||||
json.units = setDefault(json.units, serverSettings.units);
|
json.units = setDefault(json.units, serverSettings.units);
|
||||||
//console.log("browserSettings.units: " + json.units);
|
|
||||||
if (json.units == "mmol") {
|
if (json.units == "mmol") {
|
||||||
$("#mmol-browser").prop("checked", true);
|
$("#mmol-browser").prop("checked", true);
|
||||||
} else {
|
} else {
|
||||||
$("#mgdl-browser").prop("checked", true);
|
$("#mgdl-browser").prop("checked", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json.alarmHigh = setDefault(json.alarmHigh, defaultSettings.alarmHigh);
|
||||||
|
$("#alarmhigh-browser").prop("checked", json.alarmHigh);
|
||||||
|
json.alarmLow = setDefault(json.alarmLow, defaultSettings.alarmLow);
|
||||||
|
$("#alarmlow-browser").prop("checked", json.alarmLow);
|
||||||
|
|
||||||
json.nightMode = setDefault(json.nightMode, defaultSettings.nightMode);
|
json.nightMode = setDefault(json.nightMode, defaultSettings.nightMode);
|
||||||
$("#nightmode-browser").prop("checked", json.nightMode);
|
$("#nightmode-browser").prop("checked", json.nightMode);
|
||||||
|
|
||||||
if (json.customTitle) {
|
if (json.customTitle) {
|
||||||
$("h1.customTitle").html(json.customTitle);
|
$("h1.customTitle").text(json.customTitle);
|
||||||
$("input#customTitle").prop("value", json.customTitle);
|
$("input#customTitle").prop("value", json.customTitle);
|
||||||
document.title = "Nightscout: " + json.customTitle;
|
document.title = "Nightscout: " + json.customTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json.theme == "colors") {
|
||||||
|
$("#theme-colors-browser").prop("checked", true);
|
||||||
|
} else {
|
||||||
|
$("#theme-default-browser").prop("checked", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(err) {
|
catch(err) {
|
||||||
showLocalstorageError();
|
showLocalstorageError();
|
||||||
@@ -84,13 +101,24 @@ function jsonIsNotEmpty(json) {
|
|||||||
}
|
}
|
||||||
function storeInBrowser(json, storage) {
|
function storeInBrowser(json, storage) {
|
||||||
if (json.units) storage.set("units", json.units);
|
if (json.units) storage.set("units", json.units);
|
||||||
|
if (json.alarmHigh == true) {
|
||||||
|
storage.set("alarmHigh", true)
|
||||||
|
} else {
|
||||||
|
storage.set("alarmHigh", false)
|
||||||
|
}
|
||||||
|
if (json.alarmLow == true) {
|
||||||
|
storage.set("alarmLow", true)
|
||||||
|
} else {
|
||||||
|
storage.set("alarmLow", false)
|
||||||
|
}
|
||||||
if (json.nightMode == true) {
|
if (json.nightMode == true) {
|
||||||
storage.set("nightMode", true)
|
storage.set("nightMode", true)
|
||||||
} else {
|
} else {
|
||||||
storage.set("nightMode", false)
|
storage.set("nightMode", false)
|
||||||
}
|
}
|
||||||
if (json.customTitle) storage.set("customTitle", json.customTitle);
|
if (json.customTitle) storage.set("customTitle", json.customTitle);
|
||||||
event.preventDefault();
|
if (json.theme) storage.set("theme", json.theme);
|
||||||
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
function storeOnServer(json) {
|
function storeOnServer(json) {
|
||||||
if (jsonIsNotEmpty(json)) {
|
if (jsonIsNotEmpty(json)) {
|
||||||
@@ -132,6 +160,7 @@ function closeDrawer(callback) {
|
|||||||
});
|
});
|
||||||
drawerIsOpen = false;
|
drawerIsOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDrawer() {
|
function openDrawer() {
|
||||||
drawerIsOpen = true;
|
drawerIsOpen = true;
|
||||||
$("#container").animate({marginLeft: "-200px"}, 300);
|
$("#container").animate({marginLeft: "-200px"}, 300);
|
||||||
@@ -140,6 +169,30 @@ function openDrawer() {
|
|||||||
$("#drawer").animate({right: "0"}, 300);
|
$("#drawer").animate({right: "0"}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeTreatmentDrawer(callback) {
|
||||||
|
$("#container").animate({marginLeft: "0px"}, 400, callback);
|
||||||
|
$("#chartContainer").animate({marginLeft: "0px"}, 400);
|
||||||
|
$("#treatmentDrawer").animate({right: "-300px"}, 400, function() {
|
||||||
|
$("#treatmentDrawer").css("display", "none");
|
||||||
|
});
|
||||||
|
treatmentDrawerIsOpen = false;
|
||||||
|
}
|
||||||
|
function openTreatmentDrawer() {
|
||||||
|
treatmentDrawerIsOpen = true;
|
||||||
|
$("#container").animate({marginLeft: "-300px"}, 400);
|
||||||
|
$("#chartContainer").animate({marginLeft: "-300px"}, 400);
|
||||||
|
$("#treatmentDrawer").css("display", "block");
|
||||||
|
$("#treatmentDrawer").animate({right: "0"}, 400);
|
||||||
|
|
||||||
|
$('#enteredBy').val(browserStorage.get("enteredBy") || '');
|
||||||
|
$('#eventType').val('BG Check');
|
||||||
|
$('#glucoseValue').val('');
|
||||||
|
$('#meter').prop('checked', true)
|
||||||
|
$('#carbsGiven').val('');
|
||||||
|
$('#insulinGiven').val('');
|
||||||
|
$('#notes').val('');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function closeNotification() {
|
function closeNotification() {
|
||||||
var notify = $("#notification");
|
var notify = $("#notification");
|
||||||
@@ -195,6 +248,33 @@ function stretchStatusForToolbar(toolbarState){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function treatmentSubmit(event) {
|
||||||
|
|
||||||
|
var data = new Object();
|
||||||
|
data.enteredBy = document.getElementById("enteredBy").value;
|
||||||
|
data.eventType = document.getElementById("eventType").value;
|
||||||
|
data.glucose = document.getElementById("glucoseValue").value;
|
||||||
|
data.glucoseType = $('#treatment-form input[name=glucoseType]:checked').val();
|
||||||
|
data.carbs = document.getElementById("carbsGiven").value;
|
||||||
|
data.insulin = document.getElementById("insulinGiven").value;
|
||||||
|
data.notes = document.getElementById("notes").value;
|
||||||
|
|
||||||
|
var dataJson = JSON.stringify(data, null, " ");
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "/api/v1/treatments/", true);
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
xhr.send(dataJson);
|
||||||
|
|
||||||
|
browserStorage.set("enteredBy", data.enteredBy);
|
||||||
|
|
||||||
|
closeTreatmentDrawer();
|
||||||
|
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var querystring = getQueryParms();
|
var querystring = getQueryParms();
|
||||||
// var serverSettings = getServerSettings();
|
// var serverSettings = getServerSettings();
|
||||||
@@ -222,6 +302,12 @@ Dropdown.prototype.open = function (e) {
|
|||||||
|
|
||||||
|
|
||||||
$("#drawerToggle").click(function(event) {
|
$("#drawerToggle").click(function(event) {
|
||||||
|
//close other drawers
|
||||||
|
if(treatmentDrawerIsOpen) {
|
||||||
|
closeTreatmentDrawer();
|
||||||
|
treatmentDrawerIsOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(drawerIsOpen) {
|
if(drawerIsOpen) {
|
||||||
closeDrawer();
|
closeDrawer();
|
||||||
drawerIsOpen = false;
|
drawerIsOpen = false;
|
||||||
@@ -232,6 +318,25 @@ $("#drawerToggle").click(function(event) {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#treatmentDrawerToggle").click(function(event) {
|
||||||
|
//close other drawers
|
||||||
|
if(drawerIsOpen) {
|
||||||
|
closeDrawer();
|
||||||
|
drawerIsOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(treatmentDrawerIsOpen) {
|
||||||
|
closeTreatmentDrawer();
|
||||||
|
treatmentDrawerIsOpen = false;
|
||||||
|
} else {
|
||||||
|
openTreatmentDrawer();
|
||||||
|
treatmentDrawerIsOpen = true;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#treatmentDrawer button").click(treatmentSubmit);
|
||||||
|
|
||||||
$("#notification").click(function(event) {
|
$("#notification").click(function(event) {
|
||||||
closeNotification();
|
closeNotification();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -255,8 +360,11 @@ $("#showToolbar").find("a").click(function(event) {
|
|||||||
$("input#save").click(function() {
|
$("input#save").click(function() {
|
||||||
storeInBrowser({
|
storeInBrowser({
|
||||||
"units": $("input:radio[name=units-browser]:checked").val(),
|
"units": $("input:radio[name=units-browser]:checked").val(),
|
||||||
|
"alarmHigh": $("#alarmhigh-browser").prop("checked"),
|
||||||
|
"alarmLow": $("#alarmlow-browser").prop("checked"),
|
||||||
"nightMode": $("#nightmode-browser").prop("checked"),
|
"nightMode": $("#nightmode-browser").prop("checked"),
|
||||||
"customTitle": $("input#customTitle").prop("value")
|
"customTitle": $("input#customTitle").prop("value"),
|
||||||
|
"theme": $("input:radio[name=theme-browser]:checked").val()
|
||||||
}, browserStorage);
|
}, browserStorage);
|
||||||
|
|
||||||
storeOnServer({
|
storeOnServer({
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Nightscout: Treatments</title>
|
||||||
|
<link href="/images/round1.png" rel="icon" id="favicon" type="image/png" />
|
||||||
|
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||||
|
<style type="text/css">
|
||||||
|
table tr td {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="/bower_components/angularjs/angular.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var app = angular.module('ns-treatments', []);
|
||||||
|
|
||||||
|
app.controller('TreatmentsController', function ($scope, $http, $timeout, visibility) {
|
||||||
|
var pageIsHidden = false;
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
console.info("update called");
|
||||||
|
if (pageIsHidden) {
|
||||||
|
console.info('Not updating, since page is hidden');
|
||||||
|
} else {
|
||||||
|
console.info('Updating, since page is visible');
|
||||||
|
$http.get('/api/v1/treatments').success(function(treatments) {
|
||||||
|
console.info("got treatments", treatments);
|
||||||
|
$scope.treatments = treatments;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.$on('visibilityChanged', function(event, isHidden) {
|
||||||
|
console.info('changed pageIsHidden to ' + isHidden);
|
||||||
|
pageIsHidden = isHidden;
|
||||||
|
if (!pageIsHidden) update();
|
||||||
|
});
|
||||||
|
|
||||||
|
function startUpdateCycle() {
|
||||||
|
update();
|
||||||
|
$timeout(startUpdateCycle, 20 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
startUpdateCycle();
|
||||||
|
|
||||||
|
$scope.glucoseDisplay = function(treatment) {
|
||||||
|
if (treatment.glucose)
|
||||||
|
return treatment.glucose + (treatment.glucoseType ? ' (' + treatment.glucoseType + ')' : '');
|
||||||
|
else
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.service('visibility', function visibility($rootScope) {
|
||||||
|
function visibilityChanged() {
|
||||||
|
$rootScope.$broadcast('visibilityChanged', !!(document.hidden || document.webkitHidden || document.mozHidden || document.msHidden));
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('webkitvisibilitychange', visibilityChanged);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body ng-app="ns-treatments">
|
||||||
|
<div class="container" ng-controller="TreatmentsController">
|
||||||
|
<h3>Nightscout: Treatments</h3>
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Time</td>
|
||||||
|
<td>Event Type</td>
|
||||||
|
<td>BG</td>
|
||||||
|
<td>Insulin</td>
|
||||||
|
<td>Carbs</td>
|
||||||
|
<td>Entered By</td>
|
||||||
|
<td>Notes</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="treatment in treatments">
|
||||||
|
<td>{{treatment.created_at | date:'short'}}</td>
|
||||||
|
<td>{{treatment.eventType}}</td>
|
||||||
|
<td>{{glucoseDisplay(treatment)}}</td>
|
||||||
|
<td>{{treatment.insulin | number: 2}}</td>
|
||||||
|
<td>{{treatment.carbs}}</td>
|
||||||
|
<td>{{treatment.enteredBy}}</td>
|
||||||
|
<td>{{treatment.notes}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
db.treatments.find().forEach(
|
||||||
|
function (elem) {
|
||||||
|
db.treatments.update(
|
||||||
|
{
|
||||||
|
_id: elem._id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
glucose: elem.glucoseValue,
|
||||||
|
insulin: elem.insulinGiven,
|
||||||
|
carbs: elem.carbsGiven
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
'use strict';
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// This script is intended to be run as a cron job
|
||||||
|
// every n-minutes or whatever the equiv is on windows
|
||||||
|
//
|
||||||
|
// Author: John A. [euclidjda](https://github.com/euclidjda)
|
||||||
|
// Source: https://gist.github.com/euclidjda/4ae207a89921f21382a9
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// DB Connection setup and utils
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var mongodb = require('mongodb');
|
||||||
|
var software = require('./package.json');
|
||||||
|
var env = require('./env')( );
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
||||||
|
function main( ) {
|
||||||
|
|
||||||
|
var MongoClient = mongodb.MongoClient;
|
||||||
|
|
||||||
|
MongoClient.connect(env.mongo, function connected (err, db) {
|
||||||
|
|
||||||
|
console.log("Connected to mongo, ERROR: %j", err);
|
||||||
|
if (err) { throw err; }
|
||||||
|
populate_collection( db );
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function populate_collection( db ) {
|
||||||
|
|
||||||
|
//console.log( 'mongo = ' + env.mongo );
|
||||||
|
//console.log( 'collection = ' + env.mongo_collection );
|
||||||
|
|
||||||
|
var cgm_collection = db.collection( env.mongo_collection );
|
||||||
|
|
||||||
|
var new_cgm_record = get_cgm_record();
|
||||||
|
|
||||||
|
cgm_collection.insert( new_cgm_record, function(err,created) {
|
||||||
|
|
||||||
|
// TODO: Error checking
|
||||||
|
process.exit( 0 );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_cgm_record( ) {
|
||||||
|
|
||||||
|
var dateobj = new Date();
|
||||||
|
var datemil = dateobj.getTime();
|
||||||
|
var datesec = datemil / 1000;
|
||||||
|
var datestr = getDateString( dateobj );
|
||||||
|
|
||||||
|
// We put the time in a range from -1 to +1 for every thiry minute period
|
||||||
|
var range = (datesec % 1800)/900 - 1.0;
|
||||||
|
|
||||||
|
// The we push through a COS function and scale between 40 and 400 (so it is like a bg level)
|
||||||
|
var sgv = Math.floor(360*(Math.cos( 10.0 * range / 3.14 ) / 2 + 0.5)) + 40;
|
||||||
|
var dir = range > 0.0 ? "FortyFiveDown" : "FortyFiveUp";
|
||||||
|
|
||||||
|
console.log( 'Writing Record: ');
|
||||||
|
console.log( 'sgv = ' + sgv );
|
||||||
|
console.log( 'date = ' + datemil );
|
||||||
|
console.log( 'dir = ' + dir );
|
||||||
|
console.log( 'str = ' + datestr );
|
||||||
|
|
||||||
|
var mondo_db = null;
|
||||||
|
var doc = { 'device' :'dexcom' ,
|
||||||
|
'date' : datemil ,
|
||||||
|
'sgv' : sgv ,
|
||||||
|
'direction' : dir ,
|
||||||
|
'dateString' : datestr };
|
||||||
|
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDateString( d ) {
|
||||||
|
|
||||||
|
// How I wish js had strftime. This would be one line of code!
|
||||||
|
|
||||||
|
var month = d.getMonth();
|
||||||
|
var day = d.getDay();
|
||||||
|
var year = d.getFullYear();
|
||||||
|
|
||||||
|
if (month < 10 ) month = '0'+month;
|
||||||
|
if (day < 10 ) day = '0'+day;
|
||||||
|
|
||||||
|
var hour = d.getHours();
|
||||||
|
var min = d.getMinutes();
|
||||||
|
var sec = d.getSeconds();
|
||||||
|
|
||||||
|
var ampm = 'PM';
|
||||||
|
|
||||||
|
if (hour < 12)
|
||||||
|
{
|
||||||
|
ampm = "AM";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ampm = "PM";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hour == 0)
|
||||||
|
{
|
||||||
|
hour = 12;
|
||||||
|
}
|
||||||
|
if (hour > 12)
|
||||||
|
{
|
||||||
|
hour = hour - 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hour < 10) hour = '0' + hour;
|
||||||
|
if (min < 10) min = '0' + min;
|
||||||
|
if (sec < 10) sec = '0' + sec;
|
||||||
|
|
||||||
|
return month + '/' + day + '/' + year + ' ' + hour + ':' + min + ':' + sec + ' ' + ampm;
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user