diff --git a/lib/api/profile/index.js b/lib/api/profile/index.js
index 30f8fe240ca..faae33518ec 100644
--- a/lib/api/profile/index.js
+++ b/lib/api/profile/index.js
@@ -16,6 +16,31 @@ function configure (app, wares, ctx) {
api.use(wares.bodyParser.urlencoded({ extended: true }));
api.use(ctx.authorization.isPermitted('api:profile:read'));
+
+
+ /**
+ * @function query_models
+ * Perform the standard query logic, translating API parameters into mongo
+ * db queries in a fairly regimented manner.
+ * This middleware executes the query, returning the results as JSON
+ */
+ function query_models (req, res, next) {
+ var query = req.query;
+
+ // If "?count=" is present, use that number to decide how many to return.
+ if (!query.count) {
+ query.count = consts.ENTRIES_DEFAULT_COUNT;
+ }
+
+ // perform the query
+ ctx.profile.list_query(query, function payload(err, profiles) {
+ return res.json(profiles);
+ });
+ }
+
+ // List profiles available
+ api.get('/profiles/', query_models);
+
// List profiles available
api.get('/profile/', function(req, res) {
ctx.profile.list(function (err, attribute) {
diff --git a/lib/client/browser-settings.js b/lib/client/browser-settings.js
index 2119b39da40..7b24348e75a 100644
--- a/lib/client/browser-settings.js
+++ b/lib/client/browser-settings.js
@@ -256,6 +256,7 @@ function init (client, serverSettings, $) {
settings.thresholds = serverSettings.settings.thresholds;
}
+
if (serverSettings.settings.enable) {
settings.enable = serverSettings.settings.enable;
}
diff --git a/lib/profilefunctions.js b/lib/profilefunctions.js
index 8bd251d4820..5ae6435c4c1 100644
--- a/lib/profilefunctions.js
+++ b/lib/profilefunctions.js
@@ -142,22 +142,28 @@ function init (profileData) {
};
profile.getCurrentProfile = function getCurrentProfile (time, spec_profile) {
+ if (spec_profile) {
+ return spec_profile;
+ } else {
+ time = time || new Date().getTime();
+
+ time = time || Date.now();
+ var minuteTime = Math.round(time / 60000) * 60000;
+ var cacheKey = ("profile" + minuteTime + spec_profile);
+ var returnValue = cache.get(cacheKey);
+
+ if (returnValue) {
+ return returnValue;
+ }
- time = time || Date.now();
- var minuteTime = Math.round(time / 60000) * 60000;
- var cacheKey = ("profile" + minuteTime + spec_profile);
- var returnValue = cache.get(cacheKey);
+ var pdataActive = profile.profileFromTime(time);
+ var data = profile.hasData() ? pdataActive : null;
+ var timeprofile = profile.activeProfileToTime(time);
+ returnValue = data && data.store[timeprofile] ? data.store[timeprofile] : {};
- if (returnValue) {
+ cache.put(cacheKey, returnValue, cacheTTL);
return returnValue;
}
-
- var data = profile.hasData() ? profile.data[0] : null;
- var timeprofile = spec_profile || profile.activeProfileToTime(time);
- returnValue = data && data.store[timeprofile] ? data.store[timeprofile] : {};
-
- cache.put(cacheKey, returnValue, cacheTTL);
- return returnValue;
};
profile.getUnits = function getUnits (spec_profile) {
@@ -225,10 +231,13 @@ function init (profileData) {
profile.activeProfileToTime = function activeProfileToTime (time) {
if (profile.hasData()) {
- var timeprofile = profile.data[0].defaultProfile;
time = Number(time) || new Date().getTime();
+
+ var pdataActive = profile.profileFromTime(time);
+ var timeprofile = pdataActive.defaultProfile;
var treatment = profile.activeProfileTreatmentToTime(time);
- if (treatment && profile.data[0].store && profile.data[0].store[treatment.profile]) {
+
+ if (treatment && pdataActive.store && pdataActive.store[treatment.profile]) {
timeprofile = treatment.profile;
}
return timeprofile;
@@ -248,30 +257,31 @@ function init (profileData) {
var treatment = null;
if (profile.hasData()) {
- profile.profiletreatments.forEach(function eachTreatment (t) {
- if (time >= t.mills && t.mills >= profile.data[0].mills) {
- var duration = times.mins(t.duration || 0).msecs;
- if (duration != 0 && time < t.mills + duration) {
- treatment = t;
- // if profile switch contains json of profile inject it in to store to be findable by profile name
- if (treatment.profileJson && !profile.data[0].store[treatment.profile]) {
- if (treatment.profile.indexOf("@@@@@") < 0)
- treatment.profile += "@@@@@" + treatment.mills;
- let json = JSON.parse(treatment.profileJson);
- profile.data[0].store[treatment.profile] = json;
- }
- }
- if (duration == 0) {
- treatment = t;
- // if profile switch contains json of profile inject it in to store to be findable by profile name
- if (treatment.profileJson && !profile.data[0].store[treatment.profile]) {
- if (treatment.profile.indexOf("@@@@@") < 0)
- treatment.profile += "@@@@@" + treatment.mills;
- let json = JSON.parse(treatment.profileJson);
- profile.data[0].store[treatment.profile] = json;
- }
+ var pdataActive = profile.profileFromTime(time);
+ profile.profiletreatments.forEach(function eachTreatment(t) {
+ if (time >= t.mills && t.mills >= pdataActive.mills) {
+ var duration = times.mins(t.duration || 0).msecs;
+ if (duration != 0 && time < t.mills + duration) {
+ treatment = t;
+ // if profile switch contains json of profile inject it in to store to be findable by profile name
+ if (treatment.profileJson && !pdataActive.store[treatment.profile]) {
+ if (treatment.profile.indexOf("@@@@@") < 0)
+ treatment.profile += "@@@@@" + treatment.mills;
+ let json = JSON.parse(treatment.profileJson);
+ pdataActive.store[treatment.profile] = json;
+ }
+ }
+ if (duration == 0) {
+ treatment = t;
+ // if profile switch contains json of profile inject it in to store to be findable by profile name
+ if (treatment.profileJson && !pdataActive.store[treatment.profile]) {
+ if (treatment.profile.indexOf("@@@@@") < 0)
+ treatment.profile += "@@@@@" + treatment.mills;
+ let json = JSON.parse(treatment.profileJson);
+ pdataActive.store[treatment.profile] = json;
+ }
+ }
}
- }
});
}
@@ -286,6 +296,23 @@ function init (profileData) {
else return name.substring(0, index);
}
+ profile.profileFromTime = function profileFromTime (time) {
+ var profileData = null;
+
+ if (profile.hasData()) {
+ profileData = profile.data[0];
+ for (var i = 0; i < profile.data.length; i++)
+ {
+ if (Number(time) >= Number(profile.data[i].mills)) {
+ profileData = profile.data[i];
+ break;
+ }
+ }
+ }
+
+ return profileData;
+ }
+
profile.tempBasalTreatment = function tempBasalTreatment (time) {
// Most queries for the data in reporting will match the latest found value, caching that hugely improves performance
diff --git a/lib/report_plugins/daytoday.js b/lib/report_plugins/daytoday.js
index 9348bd76e8f..093cbeab042 100644
--- a/lib/report_plugins/daytoday.js
+++ b/lib/report_plugins/daytoday.js
@@ -452,6 +452,8 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
data.netBasalNegative[hour] = 0;
});
+ profile.loadData(datastorage.profiles);
+
profile.updateTreatments(datastorage.profileSwitchTreatments, datastorage.tempbasalTreatments, datastorage.combobolusTreatments);
var bolusInsulin = 0;
diff --git a/lib/report_plugins/profiles.js b/lib/report_plugins/profiles.js
index 4aea2d040a8..f3bd9eb8d21 100644
--- a/lib/report_plugins/profiles.js
+++ b/lib/report_plugins/profiles.js
@@ -90,10 +90,12 @@ profiles.report = function report_profiles (datastorage) {
function displayRanges (array, array2) {
var text = '';
- for (var i = 0; i < array.length; i++) {
- text += array[i].time + ' : ' + array[i].value + (array2 ? ' - ' + array2[i].value : '') + '
';
- }
+ if (array && array2) {
+ for (var i = 0; i < array.length; i++) {
+ text += array[i].time + ' : ' + array[i].value + (array2 ? ' - ' + array2[i].value : '') + '
';
+ }
+ }
return text;
}
};
diff --git a/lib/server/profile.js b/lib/server/profile.js
index d456b590959..e4c43504256 100644
--- a/lib/server/profile.js
+++ b/lib/server/profile.js
@@ -1,5 +1,7 @@
'use strict';
+var find_options = require('./query');
+
function storage (collection, ctx) {
var ObjectID = require('mongodb').ObjectID;
@@ -27,6 +29,48 @@ function storage (collection, ctx) {
return api( ).find({ }).sort({startDate: -1}).toArray(fn);
}
+ function list_query (opts, fn) {
+
+ storage.queryOpts = {
+ walker: {}
+ , dateField: 'startDate'
+ };
+
+ function limit () {
+ if (opts && opts.count) {
+ return this.limit(parseInt(opts.count));
+ }
+ return this;
+ }
+
+ return limit.call(api()
+ .find(query_for(opts))
+ .sort(opts && opts.sort && query_sort(opts) || { startDate: -1 }), opts)
+ .toArray(fn);
+ }
+
+ function query_for (opts) {
+ var retVal = find_options(opts, storage.queryOpts);
+ return retVal;
+ }
+
+ function query_sort (opts) {
+ if (opts && opts.sort) {
+ var sortKeys = Object.keys(opts.sort);
+
+ for (var i = 0; i < sortKeys.length; i++) {
+ if (opts.sort[sortKeys[i]] == '1') {
+ opts.sort[sortKeys[i]] = 1;
+ }
+ else {
+ opts.sort[sortKeys[i]] = -1;
+ }
+ }
+ return opts.sort;
+ }
+ }
+
+
function last (fn) {
return api().find().sort({startDate: -1}).limit(1).toArray(fn);
}
@@ -43,6 +87,7 @@ function storage (collection, ctx) {
}
api.list = list;
+ api.list_query = list_query;
api.create = create;
api.save = save;
api.remove = remove;
diff --git a/static/report/js/report.js b/static/report/js/report.js
index f7b02ec44af..b0cbd1abab8 100644
--- a/static/report/js/report.js
+++ b/static/report/js/report.js
@@ -459,11 +459,14 @@
if (loadeddays === dayscount) {
sorteddaystoshow.sort();
var from = sorteddaystoshow[0];
+ var dFrom = sorteddaystoshow[0];
+ var dTo = sorteddaystoshow[(sorteddaystoshow.length - 1)];
+
if (options.order === report_plugins.consts.ORDER_NEWESTONTOP) {
sorteddaystoshow.reverse();
}
- loadProfileSwitch(from, function loadProfileSwitchCallback() {
- loadProfiles(function loadProfilesCallback() {
+ loadProfileSwitch(dFrom, function loadProfileSwitchCallback() {
+ loadProfilesRange(dFrom, dTo, sorteddaystoshow.length, function loadProfilesCallback() {
$('#info > b').html('' + translate('Rendering') + ' ...');
window.setTimeout(function () {
showreports(options);
@@ -709,7 +712,7 @@
});
}
- function loadProfileSwitch(from, callback) {
+ function loadProfileSwitch (from, callback) {
$('#info > b').html(''+translate('Loading profile switch data') + ' ...');
var tquery = '?find[eventType]=Profile Switch' + '&find[created_at][$lte]=' + new Date(from).toISOString() + '&count=1';
$.ajax('/api/v1/treatments.json'+tquery, {
@@ -743,6 +746,69 @@
}).done(callback);
}
+ function loadProfilesRange (dateFrom, dateTo, dayCount, callback) {
+ $('#info > b').html('' + translate('Loading profile range') + ' ...');
+
+ $.when(
+ loadProfilesRangeCore(dateFrom, dateTo, dayCount),
+ loadProfilesRangePrevious(dateFrom),
+ loadProfilesRangeNext(dateTo)
+ )
+ .done(callback)
+ .fail(function () {
+ datastorage.profiles = [];
+ });
+ }
+
+ function loadProfilesRangeCore (dateFrom, dateTo, dayCount) {
+ $('#info > b').html('' + translate('Loading core profiles') + ' ...');
+
+ //The results must be returned in descending order to work with key logic in routines such as getCurrentProfile
+ var tquery = '?find[startDate][$gte]=' + new Date(dateFrom).toISOString() + '&find[startDate][$lte]=' + new Date(dateTo).toISOString() + '&sort[startDate]=-1&count=' + dayCount;
+
+ return $.ajax('/api/v1/profiles' + tquery, {
+ headers: client.headers(),
+ async: false,
+ success: function (records) {
+ datastorage.profiles = records;
+ }
+ });
+ }
+
+ function loadProfilesRangePrevious (dateFrom) {
+ $('#info > b').html('' + translate('Loading previous profile') + ' ...');
+
+ //Find first one before the start date and add to datastorage.profiles
+ var tquery = '?find[startDate][$lt]=' + new Date(dateFrom).toISOString() + '&sort[startDate]=-1&count=1';
+
+ return $.ajax('/api/v1/profiles' + tquery, {
+ headers: client.headers(),
+ async: false,
+ success: function (records) {
+ records.forEach(function (r) {
+ datastorage.profiles.push(r);
+ });
+ }
+ });
+ }
+
+ function loadProfilesRangeNext (dateTo) {
+ $('#info > b').html('' + translate('Loading next profile') + ' ...');
+
+ //Find first one after the end date and add to datastorage.profiles
+ var tquery = '?find[startDate][$gt]=' + new Date(dateTo).toISOString() + '&sort[startDate]=1&count=1';
+
+ return $.ajax('/api/v1/profiles' + tquery, {
+ headers: client.headers(),
+ async: false,
+ success: function (records) {
+ records.forEach(function (r) {
+ //must be inserted as top to maintain profiles being sorted by date in descending order
+ datastorage.profiles.unshift(r);
+ });
+ }
+ });
+ }
function processData(data, day, options, callback) {
if (daystoshow[day].treatmentsonly) {
diff --git a/tests/profile.test.js b/tests/profile.test.js
index 373f0479d9d..5928ccd2618 100644
--- a/tests/profile.test.js
+++ b/tests/profile.test.js
@@ -188,5 +188,199 @@ describe('Profile', function ( ) {
dia.should.equal(9);
});
+ var multiProfileData =
+ [
+ {
+ "startDate": "2015-06-25T00:00:00.000Z",
+ "defaultProfile": "20150625-1",
+ "store": {
+ "20150625-1": {
+ "dia": "4",
+ "timezone": moment.tz().zoneName(), //Assume these are in the localtime zone so tests pass when not on UTC time
+ "startDate": "1970-01-01T00:00:00.000Z",
+ 'sens': [
+ {
+ 'time': '00:00',
+ 'value': 12
+ },
+ {
+ 'time': '02:00',
+ 'value': 13
+ },
+ {
+ 'time': '07:00',
+ 'value': 14
+ }
+ ],
+ 'carbratio': [
+ {
+ 'time': '00:00',
+ 'value': 16
+ },
+ {
+ 'time': '06:00',
+ 'value': 15
+ },
+ {
+ 'time': '14:00',
+ 'value': 17
+ }
+ ],
+ 'carbs_hr': 30,
+ 'target_low': 4.5,
+ 'target_high': 8,
+ "units": "mmol",
+ "basal": [
+ {
+ "time": "00:00",
+ "value": "0.5",
+ "timeAsSeconds": "0"
+ },
+ {
+ "time": "09:00",
+ "value": "0.25",
+ "timeAsSeconds": "32400"
+ },
+ {
+ "time": "12:30",
+ "value": "0.9",
+ "timeAsSeconds": "45000"
+ },
+ {
+ "time": "17:00",
+ "value": "0.3",
+ "timeAsSeconds": "61200"
+ },
+ {
+ "time": "20:00",
+ "value": "1",
+ "timeAsSeconds": "72000"
+ }
+ ]
+ }
+ },
+ "units": "mmol",
+ "mills": "1435190400000"
+ },
+ {
+ "startDate": "2015-06-21T00:00:00.000Z",
+ "defaultProfile": "20190621-1",
+ "store": {
+ "20190621-1": {
+ "dia": "4",
+ "timezone": moment.tz().zoneName(), //Assume these are in the localtime zone so tests pass when not on UTC time
+ "startDate": "1970-01-01T00:00:00.000Z",
+ 'sens': [
+ {
+ 'time': '00:00',
+ 'value': 11
+ },
+ {
+ 'time': '02:00',
+ 'value': 10
+ },
+ {
+ 'time': '07:00',
+ 'value': 9
+ }
+ ],
+ 'carbratio': [
+ {
+ 'time': '00:00',
+ 'value': 12
+ },
+ {
+ 'time': '06:00',
+ 'value': 13
+ },
+ {
+ 'time': '14:00',
+ 'value': 14
+ }
+ ],
+ 'carbs_hr': 35,
+ 'target_low': 4.2,
+ 'target_high': 9,
+ "units": "mmol",
+ "basal": [
+ {
+ "time": "00:00",
+ "value": "0.3",
+ "timeAsSeconds": "0"
+ },
+ {
+ "time": "09:00",
+ "value": "0.4",
+ "timeAsSeconds": "32400"
+ },
+ {
+ "time": "12:30",
+ "value": "0.5",
+ "timeAsSeconds": "45000"
+ },
+ {
+ "time": "17:00",
+ "value": "0.6",
+ "timeAsSeconds": "61200"
+ },
+ {
+ "time": "23:00",
+ "value": "0.7",
+ "timeAsSeconds": "82800"
+ }
+ ]
+ }
+ },
+ "units": "mmol",
+ "mills": "1434844800000"
+ }
+ ];
+
+ var multiProfile = require('../lib/profilefunctions')(multiProfileData);
+
+ var noon = new Date('2015-06-22 12:00:00').getTime();
+ var threepm = new Date('2015-06-26 15:00:00').getTime();
+
+ it('should return profile units when configured', function () {
+ var value = multiProfile.getUnits();
+ value.should.equal('mmol');
+ });
+
+
+ it('should know what the basal rate is at 12:00 with multiple profiles', function () {
+ var value = multiProfile.getBasal(noon);
+ value.should.equal(0.4);
+ });
+
+ it('should know what the basal rate is at 15:00 with multiple profiles', function () {
+ var value = multiProfile.getBasal(threepm);
+ value.should.equal(0.9);
+ });
+
+ it('should know what the carbratio is at 12:00 with multiple profiles', function () {
+ var carbRatio = multiProfile.getCarbRatio(noon);
+ carbRatio.should.equal(13);
+ });
+
+ it('should know what the carbratio is at 15:00 with multiple profiles', function () {
+ var carbRatio = multiProfile.getCarbRatio(threepm);
+ carbRatio.should.equal(17);
+ });
+
+ it('should know what the sensitivity is at 12:00 with multiple profiles', function () {
+ var dia = multiProfile.getSensitivity(noon);
+ dia.should.equal(9);
+ });
+
+ it('should know what the sensitivity is at 15:00 with multiple profiles', function () {
+ var dia = multiProfile.getSensitivity(threepm);
+ dia.should.equal(14);
+ });
+
+
+ it('should select the correct profile for 15:00 with multiple profiles', function () {
+ var curProfile = multiProfile.getCurrentProfile(threepm);
+ curProfile.carbs_hr.should.equal(30);
+ });
});
\ No newline at end of file