nightscout
Version:
Nightscout acts as a web-based CGM (Continuous Glucose Monitor) to allow multiple caregivers to remotely view a patients glucose data in realtime.
198 lines (161 loc) • 4.97 kB
JavaScript
;
var es = require('event-stream');
var find_options = require('./query');
var ObjectID = require('mongodb').ObjectID;
var moment = require('moment');
/**********\
* Entries
* Encapsulate persistent storage of sgv entries.
\**********/
function storage (env, ctx) {
// TODO: Code is a little redundant.
// query for entries from storage
function list (opts, fn) {
// these functions, find, sort, and limit, are used to
// dynamically configure the request, based on the options we've
// been given
// determine sort options
function sort () {
return opts && opts.sort || { date: -1 };
}
// configure the limit portion of the current query
function limit () {
if (opts && opts.count) {
return this.limit(parseInt(opts.count));
}
return this;
}
// handle all the results
function toArray (err, entries) {
fn(err, entries);
}
// now just stitch them all together
limit.call(api()
.find(query_for(opts))
.sort(sort())
).toArray(toArray);
}
function remove (opts, fn) {
api().remove(query_for(opts), function(err, stat) {
ctx.bus.emit('data-update', {
type: 'entries'
, op: 'remove'
, count: stat.result.n
, changes: opts.find._id
});
//TODO: this is triggering a read from Mongo, we can do better
ctx.bus.emit('data-received');
fn(err, stat);
});
}
// return writable stream to lint each sgv record passing through it
// TODO: get rid of this? not doing anything now
function map () {
return es.map(function iter (item, next) {
return next(null, item);
});
}
// writable stream that persists all records
// takes function to call when done
function persist (fn) {
// receives entire list at end of stream
function done (err, result) {
// report any errors
if (err) { return fn(err, result); }
// batch insert a list of records
create(result, fn);
}
// lint and store the entire list
return es.pipeline(map(), es.writeArray(done));
}
//TODO: implement
//function update (fn) {
//}
//
// store new documents using the storage mechanism
function create (docs, fn) {
// potentially a batch insert
var firstErr = null
, numDocs = docs.length
, totalCreated = 0;
docs.forEach(function(doc) {
// Normalize dates to be in UTC, store offset in utcOffset
var _sysTime = moment(doc.dateString).isValid() ? moment.parseZone(doc.dateString) : moment(doc.date);
_sysTime = _sysTime.isValid() ? _sysTime : moment();
doc.utcOffset = _sysTime.utcOffset();
doc.sysTime = _sysTime.toISOString();
if (doc.dateString) doc.dateString = doc.sysTime;
var query = (doc.sysTime && doc.type) ? { sysTime: doc.sysTime, type: doc.type } : doc;
api().update(query, doc, { upsert: true }, function(err, updateResults) {
firstErr = firstErr || err;
if (!err) {
if (updateResults.result.upserted) {
doc._id = updateResults.result.upserted[0]._id
}
ctx.bus.emit('data-update', {
type: 'entries'
, op: 'update'
, changes: ctx.ddata.processRawDataForRuntime([doc])
});
}
if (++totalCreated === numDocs) {
//TODO: this is triggering a read from Mongo, we can do better
ctx.bus.emit('data-received');
fn(firstErr, docs);
}
});
});
}
function getEntry (id, fn) {
api().findOne({ _id: ObjectID(id) }, function(err, entry) {
if (err) {
fn(err);
} else {
fn(null, entry);
}
});
}
function query_for (opts) {
return find_options(opts, storage.queryOpts);
}
// closure to represent the API
function api () {
// obtain handle usable for querying the collection associated
// with these records
return ctx.store.collection(env.entries_collection);
}
// Expose all the useful functions
api.list = list;
api.map = map;
api.create = create;
api.remove = remove;
api.persist = persist;
api.query_for = query_for;
api.getEntry = getEntry;
api.aggregate = require('./aggregate')({}, api);
api.indexedFields = [
'date'
, 'type'
, 'sgv'
, 'mbg'
, 'sysTime'
, 'dateString'
, { 'type': 1, 'date': -1, 'dateString': 1 }
];
return api;
}
storage.queryOpts = {
walker: {
date: parseInt
, sgv: parseInt
, filtered: parseInt
, unfiltered: parseInt
, rssi: parseInt
, noise: parseInt
, mbg: parseInt
}
, useEpoch: true
};
// expose module
storage.storage = storage;
module.exports = storage;