Skip to content

Commit

Permalink
Merge branch 'release/0.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason Calabrese committed Sep 11, 2014
2 parents 1cc9c61 + 6c038f6 commit c29a27e
Show file tree
Hide file tree
Showing 41 changed files with 1,127 additions and 154 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ my.env
*.env
static/bower_components/
.*.sw?
.DS_Store

.vagrant
.vagrant
/iisnode
/web.config
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: node server.js
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ cgm-remote-monitor (a.k.a. NightScout)
[![Dependency Status](https://david-dm.org/nightscout/cgm-remote-monitor.png)](https://david-dm.org/nightscout/cgm-remote-monitor)
[![Gitter chat](https://badges.gitter.im/nightscout.png)](https://gitter.im/nightscout/public)

[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy)

This acts as a web-based CGM (Continuous Glucose Montinor) to allow
multiple caregivers to remotely view a patients glucose data in
realtime. The server reads a MongoDB which is intended to be data
Expand Down
13 changes: 13 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
5 changes: 5 additions & 0 deletions bin/post-devicestatus.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh

curl -H "Content-Type: application/json" -XPOST 'http://localhost:1337/api/v1/devicestatus/' -d '{
"uploaderBattery": 55
}'
11 changes: 11 additions & 0 deletions bin/post-treatment.sh
Original file line number Diff line number Diff line change
@@ -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..."
}'
4 changes: 3 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"name": "nightscout",
"version": "0.3.5",
"version": "0.4.0",
"dependencies": {
"angularjs": "1.3.0-beta.19",
"bootstrap": "~3.2.0",
"d3": "3.4.3",
"jquery": "2.1.0",
"jQuery-Storage-API": "~1.7.2",
Expand Down
46 changes: 36 additions & 10 deletions env.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,41 @@ function config ( ) {

env.version = software.version;
env.name = software.name;
env.DISPLAY_UNITS = process.env.DISPLAY_UNITS || 'mg/dl';
env.PORT = process.env.PORT || 1337;
env.mongo = process.env.MONGO_CONNECTION || process.env.CUSTOMCONNSTR_mongo;
env.mongo_collection = process.env.CUSTOMCONNSTR_mongo_collection || 'entries';
env.settings_collection = process.env.CUSTOMCONNSTR_mongo_settings_collection || 'settings';

env.DISPLAY_UNITS = readENV('DISPLAY_UNITS', 'mg/dl');
env.PORT = readENV('PORT', 1337);
env.mongo = readENV('MONGO_CONNECTION') || readENV('MONGO') || readENV('MONGOLAB_URI');
env.mongo_collection = readENV('MONGO_COLLECTION', 'entries');
env.settings_collection = readENV('MONGO_SETTINGS_COLLECTION', 'settings');
env.treatments_collection = readENV('MONGO_TREATMENTS_COLLECTION', 'treatments');
env.devicestatus_collection = readENV('MONGO_DEVICESTATUS_COLLECTION', 'devicestatus');

env.enable = readENV('ENABLE');

var shasum = crypto.createHash('sha1');
var useSecret = (process.env.API_SECRET && process.env.API_SECRET.length > 0);

/////////////////////////////////////////////////////////////////
// A little ugly, but we don't want to read the secret into a var
/////////////////////////////////////////////////////////////////
var useSecret = (readENV('API_SECRET') && readENV('API_SECRET').length > 0);
env.api_secret = null;
// if a passphrase was provided, get the hex digest to mint a single token
if (useSecret) {
if (process.env.API_SECRET.length < consts.MIN_PASSPHRASE_LENGTH) {
if (readENV('API_SECRET').length < consts.MIN_PASSPHRASE_LENGTH) {
var msg = ["API_SECRET should be at least", consts.MIN_PASSPHRASE_LENGTH, "characters"];
var err = new Error(msg.join(' '));
// console.error(err);
throw err;
process.exit(1);
}
shasum.update(process.env.API_SECRET);
shasum.update(readENV('API_SECRET'));
env.api_secret = shasum.digest('hex');
}

// For pushing notifications to Pushover.
env.pushover_api_token = readENV('PUSHOVER_API_TOKEN');
env.pushover_user_key = readENV('PUSHOVER_USER_KEY') || readENV('PUSHOVER_GROUP_KEY');

// TODO: clean up a bit
// Some people prefer to use a json configuration file instead.
// This allows a provided json config to override environment variables
Expand All @@ -51,8 +66,19 @@ function config ( ) {
env.mongo = DB_URL;
env.mongo_collection = DB_COLLECTION;
env.settings_collection = DB_SETTINGS_COLLECTION;
var STATIC_FILES = __dirname + '/static/';
env.static_files = process.env.NIGHTSCOUT_STATIC_FILES || STATIC_FILES;
env.static_files = readENV('NIGHTSCOUT_STATIC_FILES', __dirname + '/static/');

return env;
}

function readENV(varName, defaultValue) {
//for some reason Azure uses this prefix, maybe there is a good reason
var value = process.env['CUSTOMCONNSTR_' + varName]
|| process.env['CUSTOMCONNSTR_' + varName.toLowerCase()]
|| process.env[varName]
|| process.env[varName.toLowerCase()];

return value || defaultValue;
}

module.exports = config;
47 changes: 47 additions & 0 deletions lib/api/devicestatus/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

var consts = require('../../constants');

function configure (app, wares, devicestatus) {
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('/devicestatus/', function(req, res) {
devicestatus.list(function (err, profiles) {
return res.json(profiles);
});
});

function config_authed (app, api, wares, devicestatus) {

api.post('/devicestatus/', /*TODO: auth disabled for quick UI testing... wares.verifyAuthorization, */ function(req, res) {
var obj = req.body;
devicestatus.create(obj, 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, devicestatus);
}

return api;
}

module.exports = configure;

12 changes: 11 additions & 1 deletion lib/api/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

function create (env, entries, settings) {
function create (env, entries, settings, treatments, devicestatus) {
var express = require('express'),
app = express( )
;
Expand All @@ -18,6 +18,14 @@ function create (env, entries, settings) {
app.enable('api');
}

if (env.enable) {
env.enable.toLowerCase().split(' ').forEach(function (value) {
var enable = value.trim();
console.info("enabling feature:", enable);
app.enable(enable);
});
}

app.set('title', [app.get('name'), 'API', app.get('version')].join(' '));

// Start setting up routes
Expand All @@ -29,6 +37,8 @@ function create (env, entries, settings) {
// Entries and settings
app.use('/', require('./entries/')(app, wares, entries));
app.use('/', require('./settings/')(app, wares, settings));
app.use('/', require('./treatments/')(app, wares, treatments));
app.use('/', require('./devicestatus/')(app, wares, devicestatus));

// Status
app.use('/', require('./status')(app, wares));
Expand Down
1 change: 1 addition & 0 deletions lib/api/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function configure (app, wares) {
api.get('/status', function (req, res, next) {
var info = { status: 'ok'
, apiEnabled: app.enabled('api')
, careportalEnabled: app.enabled('api') && app.enabled('careportal')
, units: app.get('units')
, version: app.get('version')
, name: app.get('name')};
Expand Down
47 changes: 47 additions & 0 deletions lib/api/treatments/index.js
Original file line number Diff line number Diff line change
@@ -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 now, need to get login figured out... 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') && app.enabled('careportal')) {
config_authed(app, api, wares, treatments);
}

return api;
}

module.exports = configure;

34 changes: 34 additions & 0 deletions lib/devicestatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

function configure (collection, storage) {

function create (obj, fn) {
obj.created_at = (new Date( )).toISOString( );
api( ).insert(obj, function (err, doc) {
fn(null, doc);
});
}

function last(fn) {
return api( ).find({ }).sort({created_at: -1}).limit(1).toArray(function(err, entries) {
if (entries && entries.length > 0)
fn(err, entries[0]);
else
fn(err, null);
});
}

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;
api.last = last;
return api;
}
module.exports = configure;
12 changes: 9 additions & 3 deletions lib/entries.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,16 @@ function entries (name, storage) {
with_collection(function(err, collection) {
if (err) { fn(err); return; }
// potentially a batch insert
collection.insert(docs, function (err, created) {
// execute the callback
fn(err, created, docs);
var firstErr = null,
totalCreated = 0;

docs.forEach(function(doc) {
collection.update(doc, doc, {upsert: true}, function (err, created) {
firstErr = firstErr || err;
totalCreated += created;
});
});
fn(firstErr, totalCreated, docs);
});
}

Expand Down
15 changes: 13 additions & 2 deletions lib/pebble.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function directionToTrend (direction) {
function pebble (req, res) {
var FORTY_MINUTES = 2400000;
var cgmData = [ ];
var uploaderBattery;

function requestMetric() {
var units = req.query.units;
Expand Down Expand Up @@ -60,6 +61,7 @@ function pebble (req, res) {
// obj.y = element.sgv;
// obj.x = element.date;
obj.datetime = element.date;
obj.battery = uploaderBattery ? "" + uploaderBattery : undefined;
// obj.date = element.date.toString( );
cgmData.push(obj);
}
Expand All @@ -70,11 +72,20 @@ function pebble (req, res) {
res.end( );
// collection.db.close();
}
req.entries.list({count: 2}, get_latest);
req.devicestatus.last(function(err, value) {
if (!err && value) {
uploaderBattery = value.uploaderBattery;
} else {
console.error("req.devicestatus.tail", err);
}

req.entries.list({count: 2}, get_latest);
});
}
function configure (entries) {
function configure (entries, devicestatus) {
function middle (req, res, next) {
req.entries = entries;
req.devicestatus = devicestatus;
next( );
}
return [middle, pebble];
Expand Down
19 changes: 19 additions & 0 deletions lib/pushover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

var Pushover = require('pushover-notifications');

function init(env) {
if (env.pushover_api_token && env.pushover_user_key) {
return new Pushover({
token: env.pushover_api_token,
user: env.pushover_user_key,
onerror: function (err) {
console.log(err);
}
});
} else {
return null;
}
}

module.exports = init;
Loading

0 comments on commit c29a27e

Please sign in to comment.