mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
* Refactor how the levels dependency is loaded, to fix some localization issues
* Moves reporting client to the bundles
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
import './bundle.source';
|
import './bundle.source';
|
||||||
|
|
||||||
window.Nightscout.report_plugins = require('../lib/report_plugins/')();
|
console.info('Nightscout report bundle start');
|
||||||
|
|
||||||
|
window.Nightscout.report_plugins_preinit = require('../lib/report_plugins/');
|
||||||
window.Nightscout.predictions = require('../lib/report/predictions');
|
window.Nightscout.predictions = require('../lib/report/predictions');
|
||||||
|
window.Nightscout.reportclient = require('../lib/report/reportclient');
|
||||||
|
|
||||||
console.info('Nightscout report bundle ready');
|
console.info('Nightscout report bundle ready');
|
||||||
|
|
||||||
// Needed for Hot Module Replacement
|
// Needed for Hot Module Replacement
|
||||||
if(typeof(module.hot) !== 'undefined') {
|
if(typeof(module.hot) !== 'undefined') {
|
||||||
module.hot.accept()
|
module.hot.accept()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ console.info('Nightscout bundle ready');
|
|||||||
// Needed for Hot Module Replacement
|
// Needed for Hot Module Replacement
|
||||||
if(typeof(module.hot) !== 'undefined') {
|
if(typeof(module.hot) !== 'undefined') {
|
||||||
module.hot.accept()
|
module.hot.accept()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ function init (client, serverSettings, $) {
|
|||||||
//ignore these, they are always on for now
|
//ignore these, they are always on for now
|
||||||
} else {
|
} else {
|
||||||
var id = 'plugin-' + plugin.name;
|
var id = 'plugin-' + plugin.name;
|
||||||
console.log('Translated plugin is', translate(plugin.label || plugin.name));
|
|
||||||
var dd = $('<dd><input type="checkbox" id="' + id + '" value="' + plugin.name + '"/><label for="' + id + '">' + translate(plugin.label || plugin.name) + '</label></dd>');
|
var dd = $('<dd><input type="checkbox" id="' + id + '" value="' + plugin.name + '"/><label for="' + id + '">' + translate(plugin.label || plugin.name) + '</label></dd>');
|
||||||
showPluginsSettings.append(dd);
|
showPluginsSettings.append(dd);
|
||||||
dd.find('input').prop('checked', settings.showPlugins.indexOf(plugin.name) > -1);
|
dd.find('input').prop('checked', settings.showPlugins.indexOf(plugin.name) > -1);
|
||||||
|
|||||||
+3
-1
@@ -202,6 +202,7 @@ client.load = function load (serverSettings, callback) {
|
|||||||
settings: client.settings
|
settings: client.settings
|
||||||
, extendedSettings: client.settings.extendedSettings
|
, extendedSettings: client.settings.extendedSettings
|
||||||
, language: language
|
, language: language
|
||||||
|
, levels: levels
|
||||||
}).registerClientDefaults();
|
}).registerClientDefaults();
|
||||||
|
|
||||||
browserSettings.loadPluginSettings(client);
|
browserSettings.loadPluginSettings(client);
|
||||||
@@ -221,7 +222,6 @@ client.load = function load (serverSettings, callback) {
|
|||||||
data: {}
|
data: {}
|
||||||
, bus: require('../bus')(client.settings, client.ctx)
|
, bus: require('../bus')(client.settings, client.ctx)
|
||||||
, settings: client.settings
|
, settings: client.settings
|
||||||
, notifications: require('../notifications')(client.settings, client.ctx)
|
|
||||||
, pluginBase: client.plugins.base(majorPills, minorPills, statusPills, bgStatus, client.tooltip, Storages.localStorage)
|
, pluginBase: client.plugins.base(majorPills, minorPills, statusPills, bgStatus, client.tooltip, Storages.localStorage)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -229,6 +229,8 @@ client.load = function load (serverSettings, callback) {
|
|||||||
levels.translate = language.translate;
|
levels.translate = language.translate;
|
||||||
client.ctx.levels = levels;
|
client.ctx.levels = levels;
|
||||||
|
|
||||||
|
client.ctx.notifications = require('../notifications')(client.settings, client.ctx);
|
||||||
|
|
||||||
client.sbx = sandbox.clientInit(client.ctx, client.now);
|
client.sbx = sandbox.clientInit(client.ctx, client.now);
|
||||||
client.renderer = require('./renderer')(client, d3, $);
|
client.renderer = require('./renderer')(client, d3, $);
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -15,5 +15,11 @@
|
|||||||
"ONE_HOUR": 3600000,
|
"ONE_HOUR": 3600000,
|
||||||
"THREE_HOURS": 10800000,
|
"THREE_HOURS": 10800000,
|
||||||
"FOUR_HOURS": 14400000,
|
"FOUR_HOURS": 14400000,
|
||||||
"SIX_HOURS": 21600000
|
"SIX_HOURS": 21600000,
|
||||||
|
"LEVEL_URGENT": 2,
|
||||||
|
"LEVEL_WARN": 1,
|
||||||
|
"LEVEL_INFO": 0,
|
||||||
|
"LEVEL_LOW": -1,
|
||||||
|
"LEVEL_LOWEST": -2,
|
||||||
|
"LEVEL_NONE": -3
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -1,12 +1,14 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var constants = require('./constants');
|
||||||
|
|
||||||
var levels = {
|
var levels = {
|
||||||
URGENT: 2
|
URGENT: constants.LEVEL_URGENT
|
||||||
, WARN: 1
|
, WARN: constants.LEVEL_WARN
|
||||||
, INFO: 0
|
, INFO: constants.LEVEL_INFO
|
||||||
, LOW: -1
|
, LOW: constants.LEVEL_LOW
|
||||||
, LOWEST: -2
|
, LOWEST: constants.LEVEL_LOWEST
|
||||||
, NONE: -3
|
, NONE: constants.LEVEL_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
levels.language = require('./language')();
|
levels.language = require('./language')();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var levels = require('./levels');
|
|
||||||
|
|
||||||
var THIRTY_MINUTES = 30 * 60 * 1000;
|
var THIRTY_MINUTES = 30 * 60 * 1000;
|
||||||
var DEFAULT_GROUPS = ['default'];
|
var DEFAULT_GROUPS = ['default'];
|
||||||
@@ -18,6 +17,9 @@ var Alarm = function(level, group, label) {
|
|||||||
var alarms = {};
|
var alarms = {};
|
||||||
|
|
||||||
function init (env, ctx) {
|
function init (env, ctx) {
|
||||||
|
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
function notifications () {
|
function notifications () {
|
||||||
return notifications;
|
return notifications;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var levels = require('../levels');
|
|
||||||
|
|
||||||
function init(ctx) {
|
function init(ctx) {
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var bage = {
|
var bage = {
|
||||||
name: 'bage'
|
name: 'bage'
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var levels = require('../levels');
|
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
|
|
||||||
function init (ctx) {
|
function init (ctx) {
|
||||||
|
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var bwp = {
|
var bwp = {
|
||||||
name: 'bwp'
|
name: 'bwp'
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var levels = require('../levels');
|
|
||||||
|
|
||||||
function init(ctx) {
|
function init(ctx) {
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var cage = {
|
var cage = {
|
||||||
name: 'cage'
|
name: 'cage'
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var levels = require('../levels');
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
function init (ctx) {
|
function init (ctx) {
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var dbsize = {
|
var dbsize = {
|
||||||
name: 'dbsize'
|
name: 'dbsize'
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var levels = require('../levels');
|
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
|
|
||||||
function init() {
|
function init(ctx) {
|
||||||
|
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var errorcodes = {
|
var errorcodes = {
|
||||||
name: 'errorcodes'
|
name: 'errorcodes'
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var levels = require('../levels');
|
|
||||||
|
|
||||||
function init(ctx) {
|
function init(ctx) {
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var iage = {
|
var iage = {
|
||||||
name: 'iage'
|
name: 'iage'
|
||||||
|
|||||||
+1
-1
@@ -3,13 +3,13 @@
|
|||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
var levels = require('../levels');
|
|
||||||
|
|
||||||
// var ALL_STATUS_FIELDS = ['status-symbol', 'status-label', 'iob', 'freq', 'rssi']; Unused variable
|
// var ALL_STATUS_FIELDS = ['status-symbol', 'status-label', 'iob', 'freq', 'rssi']; Unused variable
|
||||||
|
|
||||||
function init (ctx) {
|
function init (ctx) {
|
||||||
var utils = require('../utils')(ctx);
|
var utils = require('../utils')(ctx);
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var loop = {
|
var loop = {
|
||||||
name: 'loop'
|
name: 'loop'
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
var levels = require('../levels');
|
|
||||||
var consts = require('../constants');
|
var consts = require('../constants');
|
||||||
|
|
||||||
// var ALL_STATUS_FIELDS = ['status-symbol', 'status-label', 'iob', 'meal-assist', 'freq', 'rssi']; Unused variable
|
// var ALL_STATUS_FIELDS = ['status-symbol', 'status-label', 'iob', 'meal-assist', 'freq', 'rssi']; Unused variable
|
||||||
@@ -17,6 +16,7 @@ function init (ctx) {
|
|||||||
};
|
};
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
var firstPrefs = true;
|
var firstPrefs = true;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
openaps.getClientPrefs = function getClientPrefs() {
|
openaps.getClientPrefs = function getClientPrefs() {
|
||||||
return ([{
|
return ([{
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var levels = require('../levels');
|
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
|
|
||||||
var ALL_STATUS_FIELDS = ['reservoir', 'battery', 'clock', 'status', 'device'];
|
var ALL_STATUS_FIELDS = ['reservoir', 'battery', 'clock', 'status', 'device'];
|
||||||
@@ -11,6 +10,7 @@ function init (ctx) {
|
|||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
var timeago = require('./timeago')(ctx);
|
var timeago = require('./timeago')(ctx);
|
||||||
var openaps = require('./openaps')(ctx);
|
var openaps = require('./openaps')(ctx);
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var pump = {
|
var pump = {
|
||||||
name: 'pump'
|
name: 'pump'
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var Pushover = require('pushover-notifications');
|
var Pushover = require('pushover-notifications');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var levels = require('../levels');
|
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
|
|
||||||
function init (env) {
|
function init (env) {
|
||||||
@@ -12,6 +11,8 @@ function init (env) {
|
|||||||
, PRIORITY_EMERGENCY: 2
|
, PRIORITY_EMERGENCY: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var levels = env.levels;
|
||||||
|
|
||||||
var pushoverAPI = setupPushover(env);
|
var pushoverAPI = setupPushover(env);
|
||||||
|
|
||||||
function selectKeys (notify) {
|
function selectKeys (notify) {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
var levels = require('../levels');
|
|
||||||
|
|
||||||
function init(ctx) {
|
function init(ctx) {
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var sage = {
|
var sage = {
|
||||||
name: 'sage'
|
name: 'sage'
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var levels = require('../levels');
|
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
|
|
||||||
function init() {
|
function init(ctx) {
|
||||||
|
|
||||||
var simplealarms = {
|
var simplealarms = {
|
||||||
name: 'simplealarms'
|
name: 'simplealarms'
|
||||||
, label: 'Simple Alarms'
|
, label: 'Simple Alarms'
|
||||||
, pluginType: 'notification'
|
, pluginType: 'notification'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
simplealarms.checkNotifications = function checkNotifications(sbx) {
|
simplealarms.checkNotifications = function checkNotifications(sbx) {
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var levels = require('../levels');
|
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
var lastChecked = new Date();
|
var lastChecked = new Date();
|
||||||
var lastRecoveryTimeFromSuspend = new Date("1900-01-01");
|
var lastRecoveryTimeFromSuspend = new Date("1900-01-01");
|
||||||
|
|
||||||
function init(ctx) {
|
function init(ctx) {
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var timeago = {
|
var timeago = {
|
||||||
name: 'timeago',
|
name: 'timeago',
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const times = require('../times');
|
const times = require('../times');
|
||||||
const simplealarms = require('./simplealarms')();
|
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
|
||||||
const MANUAL_TREATMENTS = ['BG Check', 'Meal Bolus', 'Carb Correction', 'Correction Bolus'];
|
const MANUAL_TREATMENTS = ['BG Check', 'Meal Bolus', 'Carb Correction', 'Correction Bolus'];
|
||||||
|
|
||||||
function init() {
|
function init(ctx) {
|
||||||
|
|
||||||
const treatmentnotify = {
|
const treatmentnotify = {
|
||||||
name: 'treatmentnotify'
|
name: 'treatmentnotify'
|
||||||
@@ -15,6 +14,8 @@ function init() {
|
|||||||
, pluginType: 'notification'
|
, pluginType: 'notification'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const simplealarms = require('./simplealarms')(ctx);
|
||||||
|
|
||||||
//automated treatments from OpenAPS or Loop shouldn't trigger notifications or snooze alarms
|
//automated treatments from OpenAPS or Loop shouldn't trigger notifications or snooze alarms
|
||||||
function filterTreatments (sbx) {
|
function filterTreatments (sbx) {
|
||||||
var treatments = sbx.data.treatments;
|
var treatments = sbx.data.treatments;
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
var levels = require('../levels');
|
|
||||||
|
|
||||||
function init(ctx) {
|
function init(ctx) {
|
||||||
var translate = ctx.language.translate;
|
var translate = ctx.language.translate;
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
var upbat = {
|
var upbat = {
|
||||||
name: 'upbat'
|
name: 'upbat'
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
var levels = require('../levels');
|
|
||||||
|
|
||||||
function init(ctx) {
|
function init(ctx) {
|
||||||
|
var levels = ctx.levels;
|
||||||
var utils = require('../utils')(ctx);
|
var utils = require('../utils')(ctx);
|
||||||
var firstPrefs = true;
|
var firstPrefs = true;
|
||||||
var lastStateNotification = null;
|
var lastStateNotification = null;
|
||||||
|
|||||||
@@ -0,0 +1,912 @@
|
|||||||
|
// TODO:
|
||||||
|
// - bypass nightmode in reports
|
||||||
|
// - on save/delete treatment ctx.bus.emit('data-received'); is not enough. we must add something like 'data-updated'
|
||||||
|
|
||||||
|
var init = function init () {
|
||||||
|
'use strict';
|
||||||
|
//for the tests window isn't the global object
|
||||||
|
var $ = window.$;
|
||||||
|
var _ = window._;
|
||||||
|
var moment = window.moment;
|
||||||
|
var Nightscout = window.Nightscout;
|
||||||
|
var client = Nightscout.client;
|
||||||
|
var report_plugins_preinit = Nightscout.report_plugins_preinit;
|
||||||
|
var report_plugins;
|
||||||
|
|
||||||
|
client.init(function loaded () {
|
||||||
|
|
||||||
|
report_plugins = report_plugins_preinit(client.ctx);
|
||||||
|
Nightscout.report_plugins = report_plugins;
|
||||||
|
|
||||||
|
// init HTML code
|
||||||
|
report_plugins.addHtmlFromPlugins(client);
|
||||||
|
// make show() accessible outside for treatments.js
|
||||||
|
report_plugins.show = show;
|
||||||
|
|
||||||
|
var translate = client.translate;
|
||||||
|
|
||||||
|
var maxInsulinValue = 0
|
||||||
|
, maxCarbsValue = 0
|
||||||
|
, maxDailyCarbsValue = 0;
|
||||||
|
var maxdays = 6 * 31;
|
||||||
|
var datastorage = {};
|
||||||
|
var daystoshow = {};
|
||||||
|
var sorteddaystoshow = [];
|
||||||
|
|
||||||
|
var targetBGdefault = {
|
||||||
|
'mg/dl': {
|
||||||
|
low: client.settings.thresholds.bgTargetBottom
|
||||||
|
, high: client.settings.thresholds.bgTargetTop
|
||||||
|
}
|
||||||
|
, 'mmol': {
|
||||||
|
low: client.utils.scaleMgdl(client.settings.thresholds.bgTargetBottom)
|
||||||
|
, high: client.utils.scaleMgdl(client.settings.thresholds.bgTargetTop)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var ONE_MIN_IN_MS = 60000;
|
||||||
|
|
||||||
|
prepareGUI();
|
||||||
|
|
||||||
|
// ****** FOOD CODE START ******
|
||||||
|
var food_categories = [];
|
||||||
|
var food_list = [];
|
||||||
|
|
||||||
|
var filter = {
|
||||||
|
category: ''
|
||||||
|
, subcategory: ''
|
||||||
|
, name: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
function fillFoodForm (event) {
|
||||||
|
$('#rp_category').empty().append('<option value="">' + translate('(none)') + '</option>');
|
||||||
|
Object.keys(food_categories).forEach(function eachCategory (s) {
|
||||||
|
$('#rp_category').append('<option value="' + s + '">' + s + '</option>');
|
||||||
|
});
|
||||||
|
filter.category = '';
|
||||||
|
fillFoodSubcategories();
|
||||||
|
|
||||||
|
$('#rp_category').change(fillFoodSubcategories);
|
||||||
|
$('#rp_subcategory').change(doFoodFilter);
|
||||||
|
$('#rp_name').on('input', doFoodFilter);
|
||||||
|
|
||||||
|
return maybePrevent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillFoodSubcategories (event) {
|
||||||
|
filter.category = $('#rp_category').val();
|
||||||
|
filter.subcategory = '';
|
||||||
|
$('#rp_subcategory').empty().append('<option value="">' + translate('(none)') + '</option>');
|
||||||
|
if (filter.category !== '') {
|
||||||
|
Object.keys(food_categories[filter.category] || {}).forEach(function eachSubCategory (s) {
|
||||||
|
$('#rp_subcategory').append('<option value="' + s + '">' + s + '</option>');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
doFoodFilter();
|
||||||
|
return maybePrevent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function doFoodFilter (event) {
|
||||||
|
if (event) {
|
||||||
|
filter.category = $('#rp_category').val();
|
||||||
|
filter.subcategory = $('#rp_subcategory').val();
|
||||||
|
filter.name = $('#rp_name').val();
|
||||||
|
}
|
||||||
|
$('#rp_food').empty();
|
||||||
|
for (var i = 0; i < food_list.length; i++) {
|
||||||
|
if (filter.category !== '' && food_list[i].category !== filter.category) { continue; }
|
||||||
|
if (filter.subcategory !== '' && food_list[i].subcategory !== filter.subcategory) { continue; }
|
||||||
|
if (filter.name !== '' && food_list[i].name.toLowerCase().indexOf(filter.name.toLowerCase()) < 0) { continue; }
|
||||||
|
var o = '';
|
||||||
|
o += food_list[i].name + ' | ';
|
||||||
|
o += translate('Portion') + ': ' + food_list[i].portion + ' ';
|
||||||
|
o += food_list[i].unit + ' | ';
|
||||||
|
o += translate('Carbs') + ': ' + food_list[i].carbs + ' g';
|
||||||
|
$('#rp_food').append('<option value="' + food_list[i]._id + '">' + o + '</option>');
|
||||||
|
}
|
||||||
|
|
||||||
|
return maybePrevent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#info').html('<b>' + translate('Loading food database') + ' ...</b>');
|
||||||
|
$.ajax('/api/v1/food/regular.json', {
|
||||||
|
headers: client.headers()
|
||||||
|
, success: function foodLoadSuccess (records) {
|
||||||
|
records.forEach(function(r) {
|
||||||
|
food_list.push(r);
|
||||||
|
if (r.category && !food_categories[r.category]) { food_categories[r.category] = {}; }
|
||||||
|
if (r.category && r.subcategory) { food_categories[r.category][r.subcategory] = true; }
|
||||||
|
});
|
||||||
|
fillFoodForm();
|
||||||
|
}
|
||||||
|
}).done(function() {
|
||||||
|
if (food_list.length) {
|
||||||
|
enableFoodGUI();
|
||||||
|
} else {
|
||||||
|
disableFoodGUI();
|
||||||
|
}
|
||||||
|
}).fail(function() {
|
||||||
|
disableFoodGUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
function enableFoodGUI () {
|
||||||
|
$('#info').html('');
|
||||||
|
|
||||||
|
$('.rp_foodgui').css('display', '');
|
||||||
|
$('#rp_food').change(function(event) {
|
||||||
|
$('#rp_enablefood').prop('checked', true);
|
||||||
|
return maybePrevent(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableFoodGUI () {
|
||||||
|
$('#info').html('');
|
||||||
|
$('.rp_foodgui').css('display', 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ****** FOOD CODE END ******
|
||||||
|
|
||||||
|
function prepareGUI () {
|
||||||
|
$('.presetdates').click(function(event) {
|
||||||
|
var days = $(this).attr('days');
|
||||||
|
$('#rp_enabledate').prop('checked', true);
|
||||||
|
return setDataRange(event, days);
|
||||||
|
});
|
||||||
|
$('#rp_show').click(show);
|
||||||
|
$('#rp_notes').bind('input', function(event) {
|
||||||
|
$('#rp_enablenotes').prop('checked', true);
|
||||||
|
return maybePrevent(event);
|
||||||
|
});
|
||||||
|
$('#rp_eventtype').bind('input', function(event) {
|
||||||
|
$('#rp_enableeventtype').prop('checked', true);
|
||||||
|
return maybePrevent(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
// fill careportal events
|
||||||
|
$('#rp_eventtype').empty();
|
||||||
|
_.each(client.careportal.events, function eachEvent (event) {
|
||||||
|
$('#rp_eventtype').append('<option value="' + event.val + '">' + translate(event.name) + '</option>');
|
||||||
|
});
|
||||||
|
$('#rp_eventtype').append('<option value="sensor">' + '>>> ' + translate('All sensor events') + '</option>');
|
||||||
|
|
||||||
|
$('#rp_targetlow').val(targetBGdefault[client.settings.units.toLowerCase()].low);
|
||||||
|
$('#rp_targethigh').val(targetBGdefault[client.settings.units.toLowerCase()].high);
|
||||||
|
|
||||||
|
if (client.settings.scaleY === 'linear') {
|
||||||
|
$('#rp_linear').prop('checked', true);
|
||||||
|
$('#wrp_linear').prop('checked', true);
|
||||||
|
} else {
|
||||||
|
$('#rp_log').prop('checked', true);
|
||||||
|
$('#wrp_log').prop('checked', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.menutab').click(switchreport_handler);
|
||||||
|
|
||||||
|
setDataRange(null, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sgvToColor (sgv, options) {
|
||||||
|
var color = 'darkgreen';
|
||||||
|
|
||||||
|
if (sgv > options.targetHigh) {
|
||||||
|
color = 'red';
|
||||||
|
} else if (sgv < options.targetLow) {
|
||||||
|
color = 'red';
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
function show (event) {
|
||||||
|
var options = {
|
||||||
|
width: 1000
|
||||||
|
, height: 300
|
||||||
|
, weekwidth: 1000
|
||||||
|
, weekheight: 300
|
||||||
|
, targetLow: 3.5
|
||||||
|
, targetHigh: 10
|
||||||
|
, raw: true
|
||||||
|
, notes: true
|
||||||
|
, food: true
|
||||||
|
, insulin: true
|
||||||
|
, carbs: true
|
||||||
|
, iob: true
|
||||||
|
, cob: true
|
||||||
|
, basal: true
|
||||||
|
, scale: report_plugins.consts.scaleYFromSettings(client)
|
||||||
|
, weekscale: report_plugins.consts.scaleYFromSettings(client)
|
||||||
|
, units: client.settings.units
|
||||||
|
};
|
||||||
|
|
||||||
|
// default time range if no time range specified in GUI
|
||||||
|
var zone = client.sbx.data.profile.getTimezone();
|
||||||
|
var timerange = '&find[created_at][$gte]=' + moment.tz('2000-01-01', zone).toISOString();
|
||||||
|
//console.log(timerange,zone);
|
||||||
|
options.targetLow = parseFloat($('#rp_targetlow').val().replace(',', '.'));
|
||||||
|
options.targetHigh = parseFloat($('#rp_targethigh').val().replace(',', '.'));
|
||||||
|
options.raw = $('#rp_optionsraw').is(':checked');
|
||||||
|
options.iob = $('#rp_optionsiob').is(':checked');
|
||||||
|
options.cob = $('#rp_optionscob').is(':checked');
|
||||||
|
options.openAps = $('#rp_optionsopenaps').is(':checked');
|
||||||
|
options.predicted = $('#rp_optionspredicted').is(':checked');
|
||||||
|
options.predictedTruncate = $('#rp_optionsPredictedTruncate').is(':checked');
|
||||||
|
options.basal = $('#rp_optionsbasal').is(':checked');
|
||||||
|
options.notes = $('#rp_optionsnotes').is(':checked');
|
||||||
|
options.food = $('#rp_optionsfood').is(':checked');
|
||||||
|
options.insulin = $('#rp_optionsinsulin').is(':checked');
|
||||||
|
options.insulindistribution = $('#rp_optionsdistribution').is(':checked');
|
||||||
|
options.carbs = $('#rp_optionscarbs').is(':checked');
|
||||||
|
options.scale = ($('#rp_linear').is(':checked') ? report_plugins.consts.SCALE_LINEAR : report_plugins.consts.SCALE_LOG);
|
||||||
|
options.weekscale = ($('#wrp_linear').is(':checked') ? report_plugins.consts.SCALE_LINEAR : report_plugins.consts.SCALE_LOG);
|
||||||
|
options.order = ($('#rp_oldestontop').is(':checked') ? report_plugins.consts.ORDER_OLDESTONTOP : report_plugins.consts.ORDER_NEWESTONTOP);
|
||||||
|
options.width = parseInt($('#rp_size :selected').attr('x'));
|
||||||
|
options.weekwidth = parseInt($('#wrp_size :selected').attr('x'));
|
||||||
|
options.height = parseInt($('#rp_size :selected').attr('y'));
|
||||||
|
options.weekheight = parseInt($('#wrp_size :selected').attr('y'));
|
||||||
|
options.loopalyzer = $("#loopalyzer").hasClass("selected"); // We only want to run through Loopalyzer if that tab is selected
|
||||||
|
if (options.loopalyzer) {
|
||||||
|
options.iob = true;
|
||||||
|
options.cob = true;
|
||||||
|
options.openAps = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchesneeded = 0;
|
||||||
|
|
||||||
|
// date range
|
||||||
|
function datefilter () {
|
||||||
|
if ($('#rp_enabledate').is(':checked')) {
|
||||||
|
matchesneeded++;
|
||||||
|
var from = moment.tz($('#rp_from').val().replace(/\//g, '-') + 'T00:00:00', zone);
|
||||||
|
var to = moment.tz($('#rp_to').val().replace(/\//g, '-') + 'T23:59:59', zone);
|
||||||
|
timerange = '&find[created_at][$gte]=' + from.toISOString() + '&find[created_at][$lt]=' + to.toISOString();
|
||||||
|
//console.log($('#rp_from').val(),$('#rp_to').val(),zone,timerange);
|
||||||
|
while (from <= to) {
|
||||||
|
if (daystoshow[from.format('YYYY-MM-DD')]) {
|
||||||
|
daystoshow[from.format('YYYY-MM-DD')]++;
|
||||||
|
} else {
|
||||||
|
daystoshow[from.format('YYYY-MM-DD')] = 1;
|
||||||
|
}
|
||||||
|
from.add(1, 'days');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log('Dayfilter: ',daystoshow);
|
||||||
|
foodfilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
//food filter
|
||||||
|
function foodfilter () {
|
||||||
|
if ($('#rp_enablefood').is(':checked')) {
|
||||||
|
matchesneeded++;
|
||||||
|
var _id = $('#rp_food').val();
|
||||||
|
if (_id) {
|
||||||
|
var treatmentData;
|
||||||
|
var tquery = '?find[boluscalc.foods._id]=' + _id + timerange;
|
||||||
|
$.ajax('/api/v1/treatments.json' + tquery, {
|
||||||
|
headers: client.headers()
|
||||||
|
, success: function(xhr) {
|
||||||
|
treatmentData = xhr.map(function(treatment) {
|
||||||
|
return moment.tz(treatment.created_at, zone).format('YYYY-MM-DD');
|
||||||
|
});
|
||||||
|
// unique it
|
||||||
|
treatmentData = $.grep(treatmentData, function(v, k) {
|
||||||
|
return $.inArray(v, treatmentData) === k;
|
||||||
|
});
|
||||||
|
treatmentData.sort(function(a, b) { return a > b; });
|
||||||
|
}
|
||||||
|
}).done(function() {
|
||||||
|
//console.log('Foodfilter: ',treatmentData);
|
||||||
|
for (var d = 0; d < treatmentData.length; d++) {
|
||||||
|
if (daystoshow[treatmentData[d]]) {
|
||||||
|
daystoshow[treatmentData[d]]++;
|
||||||
|
} else {
|
||||||
|
daystoshow[treatmentData[d]] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notesfilter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
notesfilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//notes filter
|
||||||
|
function notesfilter () {
|
||||||
|
if ($('#rp_enablenotes').is(':checked')) {
|
||||||
|
matchesneeded++;
|
||||||
|
var notes = $('#rp_notes').val();
|
||||||
|
if (notes) {
|
||||||
|
var treatmentData;
|
||||||
|
var tquery = '?find[notes]=/' + notes + '/i';
|
||||||
|
$.ajax('/api/v1/treatments.json' + tquery + timerange, {
|
||||||
|
headers: client.headers()
|
||||||
|
, success: function(xhr) {
|
||||||
|
treatmentData = xhr.map(function(treatment) {
|
||||||
|
return moment.tz(treatment.created_at, zone).format('YYYY-MM-DD');
|
||||||
|
});
|
||||||
|
// unique it
|
||||||
|
treatmentData = $.grep(treatmentData, function(v, k) {
|
||||||
|
return $.inArray(v, treatmentData) === k;
|
||||||
|
});
|
||||||
|
treatmentData.sort(function(a, b) { return a > b; });
|
||||||
|
}
|
||||||
|
}).done(function() {
|
||||||
|
//console.log('Notesfilter: ',treatmentData);
|
||||||
|
for (var d = 0; d < treatmentData.length; d++) {
|
||||||
|
if (daystoshow[treatmentData[d]]) {
|
||||||
|
daystoshow[treatmentData[d]]++;
|
||||||
|
} else {
|
||||||
|
daystoshow[treatmentData[d]] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventtypefilter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eventtypefilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//event type filter
|
||||||
|
function eventtypefilter () {
|
||||||
|
if ($('#rp_enableeventtype').is(':checked')) {
|
||||||
|
matchesneeded++;
|
||||||
|
var eventtype = $('#rp_eventtype').val();
|
||||||
|
if (eventtype) {
|
||||||
|
var treatmentData;
|
||||||
|
var tquery = '?find[eventType]=/' + eventtype + '/i';
|
||||||
|
$.ajax('/api/v1/treatments.json' + tquery + timerange, {
|
||||||
|
headers: client.headers()
|
||||||
|
, success: function(xhr) {
|
||||||
|
treatmentData = xhr.map(function(treatment) {
|
||||||
|
return moment.tz(treatment.created_at, zone).format('YYYY-MM-DD');
|
||||||
|
});
|
||||||
|
// unique it
|
||||||
|
treatmentData = $.grep(treatmentData, function(v, k) {
|
||||||
|
return $.inArray(v, treatmentData) === k;
|
||||||
|
});
|
||||||
|
treatmentData.sort(function(a, b) { return a > b; });
|
||||||
|
}
|
||||||
|
}).done(function() {
|
||||||
|
//console.log('Eventtypefilter: ',treatmentData);
|
||||||
|
for (var d = 0; d < treatmentData.length; d++) {
|
||||||
|
if (daystoshow[treatmentData[d]]) {
|
||||||
|
daystoshow[treatmentData[d]]++;
|
||||||
|
} else {
|
||||||
|
daystoshow[treatmentData[d]] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
daysfilter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
daysfilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function daysfilter () {
|
||||||
|
matchesneeded++;
|
||||||
|
Object.keys(daystoshow).forEach(function eachDay (d) {
|
||||||
|
var day = moment.tz(d, zone).day();
|
||||||
|
if (day === 0 && $('#rp_su').is(':checked')) { daystoshow[d]++; }
|
||||||
|
if (day === 1 && $('#rp_mo').is(':checked')) { daystoshow[d]++; }
|
||||||
|
if (day === 2 && $('#rp_tu').is(':checked')) { daystoshow[d]++; }
|
||||||
|
if (day === 3 && $('#rp_we').is(':checked')) { daystoshow[d]++; }
|
||||||
|
if (day === 4 && $('#rp_th').is(':checked')) { daystoshow[d]++; }
|
||||||
|
if (day === 5 && $('#rp_fr').is(':checked')) { daystoshow[d]++; }
|
||||||
|
if (day === 6 && $('#rp_sa').is(':checked')) { daystoshow[d]++; }
|
||||||
|
});
|
||||||
|
countDays();
|
||||||
|
addPreviousDayTreatments();
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
|
||||||
|
function display () {
|
||||||
|
var count = 0;
|
||||||
|
sorteddaystoshow = [];
|
||||||
|
$('#info').html('<b>' + translate('Loading') + ' ...</b>');
|
||||||
|
for (var d in daystoshow) {
|
||||||
|
if (count < maxdays) {
|
||||||
|
$('#info').append($('<div id="info-' + d + '"></div>'));
|
||||||
|
count++;
|
||||||
|
loadData(d, options, dataLoadedCallback);
|
||||||
|
} else {
|
||||||
|
$('#info').append($('<div>' + d + ' ' + translate('not displayed') + '.</div>'));
|
||||||
|
delete daystoshow[d];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count === 0) {
|
||||||
|
$('#info').html('<b>' + translate('Result is empty') + '</b>');
|
||||||
|
$('#rp_show').css('display', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dayscount = 0;
|
||||||
|
var loadeddays = 0;
|
||||||
|
|
||||||
|
function countDays () {
|
||||||
|
for (var d in daystoshow) {
|
||||||
|
if (daystoshow.hasOwnProperty(d)) {
|
||||||
|
if (daystoshow[d] === matchesneeded) {
|
||||||
|
if (dayscount < maxdays) {
|
||||||
|
dayscount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete daystoshow[d];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log('Total: ', daystoshow, 'Matches needed: ', matchesneeded, 'Will be loaded: ', dayscount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPreviousDayTreatments () {
|
||||||
|
for (var d in daystoshow) {
|
||||||
|
if (daystoshow.hasOwnProperty(d)) {
|
||||||
|
var day = moment.tz(d, zone);
|
||||||
|
var previous = day.subtract(1, 'days');
|
||||||
|
var formated = previous.format('YYYY-MM-DD');
|
||||||
|
if (!daystoshow[formated]) {
|
||||||
|
daystoshow[formated] = { treatmentsonly: true };
|
||||||
|
console.log('Adding ' + formated + ' for loading treatments');
|
||||||
|
dayscount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log('Total: ', daystoshow, 'Matches needed: ', matchesneeded, 'Will be loaded: ', dayscount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dataLoadedCallback (day) {
|
||||||
|
loadeddays++;
|
||||||
|
if (!daystoshow[day].treatmentsonly) {
|
||||||
|
sorteddaystoshow.push(day);
|
||||||
|
}
|
||||||
|
if (loadeddays === dayscount) {
|
||||||
|
sorteddaystoshow.sort();
|
||||||
|
var dFrom = sorteddaystoshow[0];
|
||||||
|
var dTo = sorteddaystoshow[(sorteddaystoshow.length - 1)];
|
||||||
|
|
||||||
|
if (options.order === report_plugins.consts.ORDER_NEWESTONTOP) {
|
||||||
|
sorteddaystoshow.reverse();
|
||||||
|
}
|
||||||
|
loadProfileSwitch(dFrom, function loadProfileSwitchCallback () {
|
||||||
|
loadProfilesRange(dFrom, dTo, sorteddaystoshow.length, function loadProfilesCallback () {
|
||||||
|
$('#info > b').html('<b>' + translate('Rendering') + ' ...</b>');
|
||||||
|
window.setTimeout(function() {
|
||||||
|
showreports(options);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#rp_show').css('display', 'none');
|
||||||
|
daystoshow = {};
|
||||||
|
|
||||||
|
datefilter();
|
||||||
|
return maybePrevent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showreports (options) {
|
||||||
|
// prepare some data used in more reports
|
||||||
|
datastorage.allstatsrecords = [];
|
||||||
|
datastorage.alldays = 0;
|
||||||
|
sorteddaystoshow.forEach(function eachDay (day) {
|
||||||
|
if (!daystoshow[day].treatmentsonly) {
|
||||||
|
datastorage.allstatsrecords = datastorage.allstatsrecords.concat(datastorage[day].statsrecords);
|
||||||
|
datastorage.alldays++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
options.maxInsulinValue = maxInsulinValue;
|
||||||
|
options.maxCarbsValue = maxCarbsValue;
|
||||||
|
options.maxDailyCarbsValue = maxDailyCarbsValue;
|
||||||
|
|
||||||
|
datastorage.treatments = [];
|
||||||
|
datastorage.devicestatus = [];
|
||||||
|
datastorage.combobolusTreatments = [];
|
||||||
|
datastorage.tempbasalTreatments = [];
|
||||||
|
Object.keys(daystoshow).forEach(function eachDay (day) {
|
||||||
|
datastorage.treatments = datastorage.treatments.concat(datastorage[day].treatments);
|
||||||
|
datastorage.devicestatus = datastorage.devicestatus.concat(datastorage[day].devicestatus);
|
||||||
|
datastorage.combobolusTreatments = datastorage.combobolusTreatments.concat(datastorage[day].combobolusTreatments);
|
||||||
|
datastorage.tempbasalTreatments = datastorage.tempbasalTreatments.concat(datastorage[day].tempbasalTreatments);
|
||||||
|
});
|
||||||
|
datastorage.tempbasalTreatments = Nightscout.client.ddata.processDurations(datastorage.tempbasalTreatments);
|
||||||
|
datastorage.treatments.sort(function sort (a, b) { return a.mills - b.mills; });
|
||||||
|
|
||||||
|
for (var d in daystoshow) {
|
||||||
|
if (daystoshow.hasOwnProperty(d)) {
|
||||||
|
if (daystoshow[d].treatmentsonly) {
|
||||||
|
delete daystoshow[d];
|
||||||
|
delete datastorage[d];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
report_plugins.eachPlugin(function(plugin) {
|
||||||
|
// jquery plot doesn't draw to hidden div
|
||||||
|
$('#' + plugin.name + '-placeholder').css('display', '');
|
||||||
|
|
||||||
|
console.log('Drawing ', plugin.name);
|
||||||
|
|
||||||
|
var skipRender = false;
|
||||||
|
|
||||||
|
if (plugin.name == 'daytoday' && !$('#daytoday').hasClass('selected')) skipRender = true;
|
||||||
|
if (plugin.name == 'treatments' && !$('#treatments').hasClass('selected')) skipRender = true;
|
||||||
|
if (plugin.name == 'weektoweek' && !$('#weektoweek').hasClass('selected')) skipRender = true;
|
||||||
|
if (plugin.name == 'loopalyzer' && !$('#loopalyzer').hasClass('selected')) skipRender = true;
|
||||||
|
|
||||||
|
if (skipRender) {
|
||||||
|
console.log('Skipping ', plugin.name);
|
||||||
|
} else {
|
||||||
|
plugin.report(datastorage, sorteddaystoshow, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$('#' + plugin.name).hasClass('selected')) {
|
||||||
|
$('#' + plugin.name + '-placeholder').css('display', 'none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#info').html('');
|
||||||
|
$('#rp_show').css('display', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDataRange (event, days) {
|
||||||
|
$('#rp_to').val(moment().format('YYYY-MM-DD'));
|
||||||
|
$('#rp_from').val(moment().add(-days + 1, 'days').format('YYYY-MM-DD'));
|
||||||
|
return maybePrevent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchreport_handler (event) {
|
||||||
|
var id = $(this).attr('id');
|
||||||
|
|
||||||
|
$('.menutab').removeClass('selected');
|
||||||
|
$('#' + id).addClass('selected');
|
||||||
|
|
||||||
|
$('.tabplaceholder').css('display', 'none');
|
||||||
|
$('#' + id + '-placeholder').css('display', '');
|
||||||
|
return maybePrevent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadData (day, options, callback) {
|
||||||
|
// check for loaded data
|
||||||
|
if ((options.openAps || options.predicted || options.iob || options.cob) && datastorage[day] && !datastorage[day].devicestatus.length) {
|
||||||
|
// OpenAPS requested but data not loaded. Load anyway ...
|
||||||
|
} else if (datastorage[day] && day !== moment().format('YYYY-MM-DD')) {
|
||||||
|
callback(day);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// patientData = [actual, predicted, mbg, treatment, cal, devicestatusData];
|
||||||
|
var data = {};
|
||||||
|
var cgmData = []
|
||||||
|
, mbgData = []
|
||||||
|
, treatmentData = []
|
||||||
|
, calData = [];
|
||||||
|
var from;
|
||||||
|
if (client.sbx.data.profile.getTimezone()) {
|
||||||
|
from = moment(day).tz(client.sbx.data.profile.getTimezone()).startOf('day').format('x');
|
||||||
|
} else {
|
||||||
|
from = moment(day).startOf('day').format('x');
|
||||||
|
}
|
||||||
|
from = parseInt(from);
|
||||||
|
var to = from + 1000 * 60 * 60 * 24;
|
||||||
|
|
||||||
|
function loadCGMData () {
|
||||||
|
if (daystoshow[day].treatmentsonly) {
|
||||||
|
data.sgv = [];
|
||||||
|
data.mbg = [];
|
||||||
|
data.cal = [];
|
||||||
|
return $.Deferred().resolve();
|
||||||
|
}
|
||||||
|
$('#info-' + day).html('<b>' + translate('Loading CGM data of') + ' ' + day + ' ...</b>');
|
||||||
|
var query = '?find[date][$gte]=' + from + '&find[date][$lt]=' + to + '&count=10000';
|
||||||
|
return $.ajax('/api/v1/entries.json' + query, {
|
||||||
|
headers: client.headers()
|
||||||
|
, success: function(xhr) {
|
||||||
|
xhr.forEach(function(element) {
|
||||||
|
if (element) {
|
||||||
|
if (element.mbg) {
|
||||||
|
mbgData.push({
|
||||||
|
y: element.mbg
|
||||||
|
, mills: element.date
|
||||||
|
, d: element.dateString
|
||||||
|
, device: element.device
|
||||||
|
});
|
||||||
|
} else if (element.sgv) {
|
||||||
|
cgmData.push({
|
||||||
|
y: element.sgv
|
||||||
|
, mills: element.date
|
||||||
|
, d: element.dateString
|
||||||
|
, device: element.device
|
||||||
|
, filtered: element.filtered
|
||||||
|
, unfiltered: element.unfiltered
|
||||||
|
, noise: element.noise
|
||||||
|
, rssi: element.rssi
|
||||||
|
, sgv: element.sgv
|
||||||
|
});
|
||||||
|
} else if (element.type === 'cal') {
|
||||||
|
calData.push({
|
||||||
|
mills: element.date + 1
|
||||||
|
, d: element.dateString
|
||||||
|
, scale: element.scale
|
||||||
|
, intercept: element.intercept
|
||||||
|
, slope: element.slope
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// sometimes cgm contains duplicates. uniq it.
|
||||||
|
data.sgv = cgmData.slice();
|
||||||
|
data.sgv.sort(function(a, b) { return a.mills - b.mills; });
|
||||||
|
var lastDate = 0;
|
||||||
|
data.sgv = data.sgv.filter(function(d) {
|
||||||
|
var ok = (lastDate + ONE_MIN_IN_MS) <= d.mills;
|
||||||
|
lastDate = d.mills;
|
||||||
|
if (!ok) { console.log("itm", JSON.stringify(d)); }
|
||||||
|
return ok;
|
||||||
|
});
|
||||||
|
data.mbg = mbgData.slice();
|
||||||
|
data.mbg.sort(function(a, b) { return a.mills - b.mills; });
|
||||||
|
data.cal = calData.slice();
|
||||||
|
data.cal.sort(function(a, b) { return a.mills - b.mills; });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTreatmentData () {
|
||||||
|
if (!datastorage.profileSwitchTreatments)
|
||||||
|
datastorage.profileSwitchTreatments = [];
|
||||||
|
$('#info-' + day).html('<b>' + translate('Loading treatments data of') + ' ' + day + ' ...</b>');
|
||||||
|
var tquery = '?find[created_at][$gte]=' + new Date(from).toISOString() + '&find[created_at][$lt]=' + new Date(to).toISOString() + '&count=1000';
|
||||||
|
return $.ajax('/api/v1/treatments.json' + tquery, {
|
||||||
|
headers: client.headers()
|
||||||
|
, cache: false
|
||||||
|
, success: function(xhr) {
|
||||||
|
treatmentData = xhr.map(function(treatment) {
|
||||||
|
var timestamp = new Date(treatment.timestamp || treatment.created_at);
|
||||||
|
treatment.mills = timestamp.getTime();
|
||||||
|
return treatment;
|
||||||
|
});
|
||||||
|
data.treatments = treatmentData.slice();
|
||||||
|
data.treatments.sort(function(a, b) { return a.mills - b.mills; });
|
||||||
|
// filter 'Combo Bolus' events
|
||||||
|
data.combobolusTreatments = data.treatments.filter(function filterComboBoluses (t) {
|
||||||
|
return t.eventType === 'Combo Bolus';
|
||||||
|
});
|
||||||
|
// filter temp basal treatments
|
||||||
|
data.tempbasalTreatments = data.treatments.filter(function filterTempBasals (t) {
|
||||||
|
return t.eventType === 'Temp Basal';
|
||||||
|
});
|
||||||
|
// filter profile switch treatments
|
||||||
|
var profileSwitch = data.treatments.filter(function filterProfileSwitch (t) {
|
||||||
|
return t.eventType === 'Profile Switch';
|
||||||
|
});
|
||||||
|
datastorage.profileSwitchTreatments = datastorage.profileSwitchTreatments.concat(profileSwitch);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDevicestatusData () {
|
||||||
|
if (daystoshow[day].treatmentsonly) {
|
||||||
|
data.devicestatus = [];
|
||||||
|
return $.Deferred().resolve();
|
||||||
|
}
|
||||||
|
if (options.iob || options.cob || options.openAps || options.predicted) {
|
||||||
|
$('#info-' + day).html('<b>' + translate('Loading device status data of') + ' ' + day + ' ...</b>');
|
||||||
|
var tquery = '?find[created_at][$gte]=' + new Date(from).toISOString() + '&find[created_at][$lt]=' + new Date(to).toISOString() + '&count=10000';
|
||||||
|
return $.ajax('/api/v1/devicestatus.json' + tquery, {
|
||||||
|
headers: client.headers()
|
||||||
|
, success: function(xhr) {
|
||||||
|
data.devicestatus = xhr.map(function(devicestatus) {
|
||||||
|
devicestatus.mills = new Date(devicestatus.timestamp || devicestatus.created_at).getTime();
|
||||||
|
return devicestatus;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.devicestatus = [];
|
||||||
|
return $.Deferred().resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$.when(loadCGMData(), loadTreatmentData(), loadDevicestatusData()).done(function() {
|
||||||
|
$('#info-' + day).html('<b>' + translate('Processing data of') + ' ' + day + ' ...</b>');
|
||||||
|
processData(data, day, options, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadProfileSwitch (from, callback) {
|
||||||
|
$('#info > b').html('<b>' + translate('Loading profile switch data') + ' ...</b>');
|
||||||
|
var tquery = '?find[eventType]=Profile Switch' + '&find[created_at][$lte]=' + new Date(from).toISOString() + '&count=1';
|
||||||
|
$.ajax('/api/v1/treatments.json' + tquery, {
|
||||||
|
headers: client.headers()
|
||||||
|
, success: function(xhr) {
|
||||||
|
var treatmentData = xhr.map(function(treatment) {
|
||||||
|
var timestamp = new Date(treatment.timestamp || treatment.created_at);
|
||||||
|
treatment.mills = timestamp.getTime();
|
||||||
|
return treatment;
|
||||||
|
});
|
||||||
|
if (!datastorage.profileSwitchTreatments)
|
||||||
|
datastorage.profileSwitchTreatments = [];
|
||||||
|
datastorage.profileSwitchTreatments = datastorage.profileSwitchTreatments.concat(treatmentData);
|
||||||
|
datastorage.profileSwitchTreatments.sort(function(a, b) { return a.mills - b.mills; });
|
||||||
|
}
|
||||||
|
}).done(function() {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadProfilesRange (dateFrom, dateTo, dayCount, callback) {
|
||||||
|
$('#info > b').html('<b>' + translate('Loading profile range') + ' ...</b>');
|
||||||
|
|
||||||
|
$.when(
|
||||||
|
loadProfilesRangeCore(dateFrom, dateTo, dayCount)
|
||||||
|
, loadProfilesRangePrevious(dateFrom)
|
||||||
|
, loadProfilesRangeNext(dateTo)
|
||||||
|
)
|
||||||
|
.done(callback)
|
||||||
|
.fail(function() {
|
||||||
|
datastorage.profiles = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadProfilesRangeCore (dateFrom, dateTo, dayCount) {
|
||||||
|
$('#info > b').html('<b>' + translate('Loading core profiles') + ' ...</b>');
|
||||||
|
|
||||||
|
//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=1000';
|
||||||
|
|
||||||
|
return $.ajax('/api/v1/profiles' + tquery, {
|
||||||
|
headers: client.headers()
|
||||||
|
, async: false
|
||||||
|
, success: function(records) {
|
||||||
|
datastorage.profiles = records;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadProfilesRangePrevious (dateFrom) {
|
||||||
|
$('#info > b').html('<b>' + translate('Loading previous profile') + ' ...</b>');
|
||||||
|
|
||||||
|
//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('<b>' + translate('Loading next profile') + ' ...</b>');
|
||||||
|
|
||||||
|
//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) {
|
||||||
|
datastorage[day] = data;
|
||||||
|
$('#info-' + day).html('');
|
||||||
|
callback(day);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// treatments
|
||||||
|
data.dailyCarbs = 0;
|
||||||
|
data.dailyProtein = 0;
|
||||||
|
data.dailyFat = 0;
|
||||||
|
|
||||||
|
data.treatments.forEach(function(d) {
|
||||||
|
if (parseFloat(d.insulin) > maxInsulinValue) {
|
||||||
|
maxInsulinValue = parseFloat(d.insulin);
|
||||||
|
}
|
||||||
|
if (parseFloat(d.carbs) > maxCarbsValue) {
|
||||||
|
maxCarbsValue = parseFloat(d.carbs);
|
||||||
|
}
|
||||||
|
if (d.carbs) {
|
||||||
|
data.dailyCarbs += Number(d.carbs);
|
||||||
|
}
|
||||||
|
if (d.protein) {
|
||||||
|
data.dailyProtein += Number(d.protein);
|
||||||
|
}
|
||||||
|
if (d.fat) {
|
||||||
|
data.dailyFat += Number(d.fat);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (data.dailyCarbs > maxDailyCarbsValue) {
|
||||||
|
maxDailyCarbsValue = data.dailyCarbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cal = data.cal[data.cal.length - 1];
|
||||||
|
var temp1 = [];
|
||||||
|
var rawbg = client.rawbg;
|
||||||
|
if (cal) {
|
||||||
|
temp1 = data.sgv.map(function(entry) {
|
||||||
|
entry.mgdl = entry.y; // value names changed from enchilada
|
||||||
|
var rawBg = rawbg.calc(entry, cal);
|
||||||
|
return { mills: entry.mills, date: new Date(entry.mills - 2 * 1000), y: rawBg, sgv: client.utils.scaleMgdl(rawBg), color: 'gray', type: 'rawbg', filtered: entry.filtered, unfiltered: entry.unfiltered };
|
||||||
|
}).filter(function(entry) { return entry.y > 0 });
|
||||||
|
}
|
||||||
|
var temp2 = data.sgv.map(function(obj) {
|
||||||
|
return { mills: obj.mills, date: new Date(obj.mills), y: obj.y, sgv: client.utils.scaleMgdl(obj.y), color: sgvToColor(client.utils.scaleMgdl(obj.y), options), type: 'sgv', noise: obj.noise, filtered: obj.filtered, unfiltered: obj.unfiltered };
|
||||||
|
});
|
||||||
|
data.sgv = [].concat(temp1, temp2);
|
||||||
|
|
||||||
|
//Add MBG's also, pretend they are SGV's
|
||||||
|
data.sgv = data.sgv.concat(data.mbg.map(function(obj) { return { date: new Date(obj.mills), y: obj.y, sgv: client.utils.scaleMgdl(obj.y), color: 'red', type: 'mbg', device: obj.device } }));
|
||||||
|
|
||||||
|
// make sure data range will be exactly 24h
|
||||||
|
var from;
|
||||||
|
if (client.sbx.data.profile.getTimezone()) {
|
||||||
|
from = moment(day).tz(client.sbx.data.profile.getTimezone()).startOf('day').toDate();
|
||||||
|
} else {
|
||||||
|
from = moment(day).startOf('day').toDate();
|
||||||
|
}
|
||||||
|
var to = new Date(from.getTime() + 1000 * 60 * 60 * 24);
|
||||||
|
data.sgv.push({ date: from, y: 40, sgv: 40, color: 'transparent', type: 'rawbg' });
|
||||||
|
data.sgv.push({ date: to, y: 40, sgv: 40, color: 'transparent', type: 'rawbg' });
|
||||||
|
|
||||||
|
// clear error data. we don't need it to display them
|
||||||
|
data.sgv = data.sgv.filter(function(d) {
|
||||||
|
if (d.y < 39) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
data.sgv = data.sgv.map(function eachSgv (sgv) {
|
||||||
|
var status = _.find(data.devicestatus, function(d) {
|
||||||
|
return d.mills >= sgv.mills && d.mills < sgv.mills + 5 * 60 * 1000;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status && status.openaps) {
|
||||||
|
sgv.openaps = status.openaps;
|
||||||
|
}
|
||||||
|
return sgv;
|
||||||
|
});
|
||||||
|
|
||||||
|
// for other reports
|
||||||
|
data.statsrecords = data.sgv.filter(function(r) {
|
||||||
|
if (r.type) {
|
||||||
|
return r.type === 'sgv';
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}).map(function(r) {
|
||||||
|
var ret = {};
|
||||||
|
ret.sgv = parseFloat(r.sgv);
|
||||||
|
ret.bgValue = parseInt(r.y);
|
||||||
|
ret.displayTime = r.date;
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
datastorage[day] = data;
|
||||||
|
$('#info-' + day).html('');
|
||||||
|
callback(day);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybePrevent (event) {
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = init;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
function init() {
|
function init(ctx) {
|
||||||
var consts = {
|
var consts = {
|
||||||
SCALE_LINEAR: 0
|
SCALE_LINEAR: 0
|
||||||
, SCALE_LOG: 1
|
, SCALE_LOG: 1
|
||||||
@@ -44,7 +44,7 @@ function init() {
|
|||||||
plugins.utils = require('./utils')({
|
plugins.utils = require('./utils')({
|
||||||
//TODO: refactor so all this happens after init somehow
|
//TODO: refactor so all this happens after init somehow
|
||||||
// until then use some defaults so things don't blow up
|
// until then use some defaults so things don't blow up
|
||||||
language: require('../language')()
|
language: ctx.language
|
||||||
, settings: {}
|
, settings: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ function boot (env, language) {
|
|||||||
ctx.plugins = require('../plugins')({
|
ctx.plugins = require('../plugins')({
|
||||||
settings: env.settings
|
settings: env.settings
|
||||||
, language: ctx.language
|
, language: ctx.language
|
||||||
|
, levels: ctx.levels
|
||||||
}).registerServerDefaults();
|
}).registerServerDefaults();
|
||||||
|
|
||||||
ctx.pushover = require('../plugins/pushover')(env);
|
ctx.pushover = require('../plugins/pushover')(env);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ const _ = require('lodash');
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const NodeCache = require('node-cache');
|
const NodeCache = require('node-cache');
|
||||||
|
|
||||||
const levels = require('../levels');
|
|
||||||
const times = require('../times');
|
const times = require('../times');
|
||||||
|
|
||||||
function init (env, ctx) {
|
function init (env, ctx) {
|
||||||
@@ -12,7 +11,7 @@ function init (env, ctx) {
|
|||||||
function pushnotify () {
|
function pushnotify () {
|
||||||
return pushnotify;
|
return pushnotify;
|
||||||
}
|
}
|
||||||
|
var levels = ctx.levels;
|
||||||
var receipts = new NodeCache({ stdTTL: times.hour().secs, checkperiod: times.mins(5).secs });
|
var receipts = new NodeCache({ stdTTL: times.hour().secs, checkperiod: times.mins(5).secs });
|
||||||
var recentlySent = new NodeCache({ stdTTL: times.mins(15).secs, checkperiod: 20 });
|
var recentlySent = new NodeCache({ stdTTL: times.mins(15).secs, checkperiod: 20 });
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var levels = require('../levels');
|
|
||||||
var times = require('../times');
|
var times = require('../times');
|
||||||
var calcData = require('../data/calcdelta');
|
var calcData = require('../data/calcdelta');
|
||||||
var ObjectID = require('mongodb').ObjectID;
|
var ObjectID = require('mongodb').ObjectID;
|
||||||
@@ -11,6 +10,8 @@ function init (env, ctx, server) {
|
|||||||
return websocket;
|
return websocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var levels = ctx.levels;
|
||||||
|
|
||||||
//var log_yellow = '\x1B[33m';
|
//var log_yellow = '\x1B[33m';
|
||||||
var log_green = '\x1B[32m';
|
var log_green = '\x1B[32m';
|
||||||
var log_magenta = '\x1B[35m';
|
var log_magenta = '\x1B[35m';
|
||||||
|
|||||||
+3
-4
@@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var levels = require('./levels');
|
|
||||||
var constants = require('./constants.json');
|
var constants = require('./constants.json');
|
||||||
|
|
||||||
function init () {
|
function init () {
|
||||||
@@ -341,7 +340,7 @@ function init () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isUrgentHighAlarmEnabled(notify) {
|
function isUrgentHighAlarmEnabled(notify) {
|
||||||
return notify.eventName === 'high' && notify.level === levels.URGENT && settings.alarmUrgentHigh;
|
return notify.eventName === 'high' && notify.level === constants.LEVEL_URGENT && settings.alarmUrgentHigh;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHighAlarmEnabled(notify) {
|
function isHighAlarmEnabled(notify) {
|
||||||
@@ -349,7 +348,7 @@ function init () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isUrgentLowAlarmEnabled(notify) {
|
function isUrgentLowAlarmEnabled(notify) {
|
||||||
return notify.eventName === 'low' && notify.level === levels.URGENT && settings.alarmUrgentLow;
|
return notify.eventName === 'low' && notify.level === constants.LEVEL_URGENT && settings.alarmUrgentLow;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLowAlarmEnabled(notify) {
|
function isLowAlarmEnabled(notify) {
|
||||||
@@ -375,7 +374,7 @@ function init () {
|
|||||||
snoozeTime = settings.alarmUrgentLowMins;
|
snoozeTime = settings.alarmUrgentLowMins;
|
||||||
} else if (isLowAlarmEnabled(notify)) {
|
} else if (isLowAlarmEnabled(notify)) {
|
||||||
snoozeTime = settings.alarmLowMins;
|
snoozeTime = settings.alarmLowMins;
|
||||||
} else if (notify.level === levels.URGENT) {
|
} else if (notify.level === constants.LEVEL_URGENT) {
|
||||||
snoozeTime = settings.alarmUrgentMins;
|
snoozeTime = settings.alarmUrgentMins;
|
||||||
} else {
|
} else {
|
||||||
snoozeTime = settings.alarmWarnMins;
|
snoozeTime = settings.alarmWarnMins;
|
||||||
|
|||||||
+5
-921
@@ -1,922 +1,6 @@
|
|||||||
// TODO:
|
'use strict';
|
||||||
// - bypass nightmode in reports
|
|
||||||
// - on save/delete treatment ctx.bus.emit('data-received'); is not enough. we must add something like 'data-updated'
|
|
||||||
|
|
||||||
(function () {
|
$(document).ready(function() {
|
||||||
'use strict';
|
console.log('Application got ready event');
|
||||||
//for the tests window isn't the global object
|
window.Nightscout.reportclient();
|
||||||
var $ = window.$;
|
});
|
||||||
var _ = window._;
|
|
||||||
var moment = window.moment;
|
|
||||||
var Nightscout = window.Nightscout;
|
|
||||||
var client = Nightscout.client;
|
|
||||||
var report_plugins = Nightscout.report_plugins;
|
|
||||||
|
|
||||||
client.init(function loaded () {
|
|
||||||
|
|
||||||
// init HTML code
|
|
||||||
report_plugins.addHtmlFromPlugins( client );
|
|
||||||
// make show() accessible outside for treatments.js
|
|
||||||
report_plugins.show = show;
|
|
||||||
|
|
||||||
var translate = client.translate;
|
|
||||||
|
|
||||||
var maxInsulinValue = 0
|
|
||||||
,maxCarbsValue = 0
|
|
||||||
,maxDailyCarbsValue = 0;
|
|
||||||
var maxdays = 6 * 31;
|
|
||||||
var datastorage = {};
|
|
||||||
var daystoshow = {};
|
|
||||||
var sorteddaystoshow = [];
|
|
||||||
|
|
||||||
var targetBGdefault = {
|
|
||||||
'mg/dl': {
|
|
||||||
low: client.settings.thresholds.bgTargetBottom
|
|
||||||
, high: client.settings.thresholds.bgTargetTop
|
|
||||||
}
|
|
||||||
, 'mmol': {
|
|
||||||
low: client.utils.scaleMgdl(client.settings.thresholds.bgTargetBottom)
|
|
||||||
, high: client.utils.scaleMgdl(client.settings.thresholds.bgTargetTop)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var ONE_MIN_IN_MS = 60000;
|
|
||||||
|
|
||||||
prepareGUI();
|
|
||||||
|
|
||||||
// ****** FOOD CODE START ******
|
|
||||||
var food_categories = [];
|
|
||||||
var food_list = [];
|
|
||||||
|
|
||||||
var filter = {
|
|
||||||
category: ''
|
|
||||||
, subcategory: ''
|
|
||||||
, name: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
function fillFoodForm(event) {
|
|
||||||
$('#rp_category').empty().append('<option value="">' + translate('(none)') + '</option>');
|
|
||||||
Object.keys(food_categories).forEach(function eachCategory(s) {
|
|
||||||
$('#rp_category').append('<option value="' + s + '">' + s + '</option>');
|
|
||||||
});
|
|
||||||
filter.category = '';
|
|
||||||
fillFoodSubcategories();
|
|
||||||
|
|
||||||
$('#rp_category').change(fillFoodSubcategories);
|
|
||||||
$('#rp_subcategory').change(doFoodFilter);
|
|
||||||
$('#rp_name').on('input',doFoodFilter);
|
|
||||||
|
|
||||||
return maybePrevent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillFoodSubcategories(event) {
|
|
||||||
filter.category = $('#rp_category').val();
|
|
||||||
filter.subcategory = '';
|
|
||||||
$('#rp_subcategory').empty().append('<option value="">' + translate('(none)') + '</option>');
|
|
||||||
if (filter.category !== '') {
|
|
||||||
Object.keys(food_categories[filter.category] || {}).forEach(function eachSubCategory(s) {
|
|
||||||
$('#rp_subcategory').append('<option value="' + s + '">' + s + '</option>');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
doFoodFilter();
|
|
||||||
return maybePrevent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function doFoodFilter(event) {
|
|
||||||
if (event) {
|
|
||||||
filter.category = $('#rp_category').val();
|
|
||||||
filter.subcategory = $('#rp_subcategory').val();
|
|
||||||
filter.name = $('#rp_name').val();
|
|
||||||
}
|
|
||||||
$('#rp_food').empty();
|
|
||||||
for (var i=0; i<food_list.length; i++) {
|
|
||||||
if (filter.category !== '' && food_list[i].category !== filter.category) { continue; }
|
|
||||||
if (filter.subcategory !== '' && food_list[i].subcategory !== filter.subcategory) { continue; }
|
|
||||||
if (filter.name !== '' && food_list[i].name.toLowerCase().indexOf(filter.name.toLowerCase()) < 0) { continue; }
|
|
||||||
var o = '';
|
|
||||||
o += food_list[i].name + ' | ';
|
|
||||||
o += translate('Portion')+': ' + food_list[i].portion + ' ';
|
|
||||||
o += food_list[i].unit + ' | ';
|
|
||||||
o += translate('Carbs')+': ' + food_list[i].carbs+' g';
|
|
||||||
$('#rp_food').append('<option value="' + food_list[i]._id + '">' + o + '</option>');
|
|
||||||
}
|
|
||||||
|
|
||||||
return maybePrevent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#info').html('<b>'+translate('Loading food database')+' ...</b>');
|
|
||||||
$.ajax('/api/v1/food/regular.json', {
|
|
||||||
headers: client.headers()
|
|
||||||
, success: function foodLoadSuccess(records) {
|
|
||||||
records.forEach(function (r) {
|
|
||||||
food_list.push(r);
|
|
||||||
if (r.category && !food_categories[r.category]) { food_categories[r.category] = {}; }
|
|
||||||
if (r.category && r.subcategory) { food_categories[r.category][r.subcategory] = true; }
|
|
||||||
});
|
|
||||||
fillFoodForm();
|
|
||||||
}
|
|
||||||
}).done(function() {
|
|
||||||
if (food_list.length) {
|
|
||||||
enableFoodGUI();
|
|
||||||
} else {
|
|
||||||
disableFoodGUI();
|
|
||||||
}
|
|
||||||
}).fail(function() {
|
|
||||||
disableFoodGUI();
|
|
||||||
});
|
|
||||||
|
|
||||||
function enableFoodGUI( ) {
|
|
||||||
$('#info').html('');
|
|
||||||
|
|
||||||
$('.rp_foodgui').css('display','');
|
|
||||||
$('#rp_food').change(function (event) {
|
|
||||||
$('#rp_enablefood').prop('checked',true);
|
|
||||||
return maybePrevent(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function disableFoodGUI(){
|
|
||||||
$('#info').html('');
|
|
||||||
$('.rp_foodgui').css('display','none');
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****** FOOD CODE END ******
|
|
||||||
|
|
||||||
function prepareGUI() {
|
|
||||||
$('.presetdates').click(function(event) {
|
|
||||||
var days = $(this).attr('days');
|
|
||||||
$('#rp_enabledate').prop('checked',true);
|
|
||||||
return setDataRange(event,days);
|
|
||||||
});
|
|
||||||
$('#rp_show').click(show);
|
|
||||||
$('#rp_notes').bind('input', function (event) {
|
|
||||||
$('#rp_enablenotes').prop('checked',true);
|
|
||||||
return maybePrevent(event);
|
|
||||||
});
|
|
||||||
$('#rp_eventtype').bind('input', function (event) {
|
|
||||||
$('#rp_enableeventtype').prop('checked',true);
|
|
||||||
return maybePrevent(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
// fill careportal events
|
|
||||||
$('#rp_eventtype').empty();
|
|
||||||
_.each(client.careportal.events, function eachEvent(event) {
|
|
||||||
$('#rp_eventtype').append('<option value="' + event.val+ '">' + translate(event.name) + '</option>');
|
|
||||||
});
|
|
||||||
$('#rp_eventtype').append('<option value="sensor">' + '>>> ' + translate('All sensor events') + '</option>');
|
|
||||||
|
|
||||||
$('#rp_targetlow').val(targetBGdefault[client.settings.units.toLowerCase()].low);
|
|
||||||
$('#rp_targethigh').val(targetBGdefault[client.settings.units.toLowerCase()].high);
|
|
||||||
|
|
||||||
if (client.settings.scaleY === 'linear') {
|
|
||||||
$('#rp_linear').prop('checked', true);
|
|
||||||
$('#wrp_linear').prop('checked', true);
|
|
||||||
} else {
|
|
||||||
$('#rp_log').prop('checked', true);
|
|
||||||
$('#wrp_log').prop('checked', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.menutab').click(switchreport_handler);
|
|
||||||
|
|
||||||
setDataRange(null,7);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sgvToColor(sgv,options) {
|
|
||||||
var color = 'darkgreen';
|
|
||||||
|
|
||||||
if (sgv > options.targetHigh) {
|
|
||||||
color = 'red';
|
|
||||||
} else if (sgv < options.targetLow) {
|
|
||||||
color = 'red';
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
function show(event) {
|
|
||||||
var options = {
|
|
||||||
width: 1000
|
|
||||||
, height: 300
|
|
||||||
, weekwidth: 1000
|
|
||||||
, weekheight: 300
|
|
||||||
, targetLow: 3.5
|
|
||||||
, targetHigh: 10
|
|
||||||
, raw: true
|
|
||||||
, notes: true
|
|
||||||
, food: true
|
|
||||||
, insulin: true
|
|
||||||
, carbs: true
|
|
||||||
, iob : true
|
|
||||||
, cob : true
|
|
||||||
, basal : true
|
|
||||||
, scale: report_plugins.consts.scaleYFromSettings(client)
|
|
||||||
, weekscale: report_plugins.consts.scaleYFromSettings(client)
|
|
||||||
, units: client.settings.units
|
|
||||||
};
|
|
||||||
|
|
||||||
// default time range if no time range specified in GUI
|
|
||||||
var zone = client.sbx.data.profile.getTimezone();
|
|
||||||
var timerange = '&find[created_at][$gte]='+moment.tz('2000-01-01',zone).toISOString();
|
|
||||||
//console.log(timerange,zone);
|
|
||||||
options.targetLow = parseFloat($('#rp_targetlow').val().replace(',','.'));
|
|
||||||
options.targetHigh = parseFloat($('#rp_targethigh').val().replace(',','.'));
|
|
||||||
options.raw = $('#rp_optionsraw').is(':checked');
|
|
||||||
options.iob = $('#rp_optionsiob').is(':checked');
|
|
||||||
options.cob = $('#rp_optionscob').is(':checked');
|
|
||||||
options.openAps = $('#rp_optionsopenaps').is(':checked');
|
|
||||||
options.predicted = $('#rp_optionspredicted').is(':checked');
|
|
||||||
options.predictedTruncate = $('#rp_optionsPredictedTruncate').is(':checked');
|
|
||||||
options.basal = $('#rp_optionsbasal').is(':checked');
|
|
||||||
options.notes = $('#rp_optionsnotes').is(':checked');
|
|
||||||
options.food = $('#rp_optionsfood').is(':checked');
|
|
||||||
options.insulin = $('#rp_optionsinsulin').is(':checked');
|
|
||||||
options.insulindistribution = $('#rp_optionsdistribution').is(':checked');
|
|
||||||
options.carbs = $('#rp_optionscarbs').is(':checked');
|
|
||||||
options.scale = ( $('#rp_linear').is(':checked') ? report_plugins.consts.SCALE_LINEAR : report_plugins.consts.SCALE_LOG );
|
|
||||||
options.weekscale = ( $('#wrp_linear').is(':checked') ? report_plugins.consts.SCALE_LINEAR : report_plugins.consts.SCALE_LOG );
|
|
||||||
options.order = ( $('#rp_oldestontop').is(':checked') ? report_plugins.consts.ORDER_OLDESTONTOP : report_plugins.consts.ORDER_NEWESTONTOP );
|
|
||||||
options.width = parseInt($('#rp_size :selected').attr('x'));
|
|
||||||
options.weekwidth = parseInt($('#wrp_size :selected').attr('x'));
|
|
||||||
options.height = parseInt($('#rp_size :selected').attr('y'));
|
|
||||||
options.weekheight = parseInt($('#wrp_size :selected').attr('y'));
|
|
||||||
options.loopalyzer = $("#loopalyzer").hasClass( "selected" ); // We only want to run through Loopalyzer if that tab is selected
|
|
||||||
if (options.loopalyzer) {
|
|
||||||
options.iob = true;
|
|
||||||
options.cob = true;
|
|
||||||
options.openAps = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var matchesneeded = 0;
|
|
||||||
|
|
||||||
// date range
|
|
||||||
function datefilter() {
|
|
||||||
if ($('#rp_enabledate').is(':checked')) {
|
|
||||||
matchesneeded++;
|
|
||||||
var from = moment.tz($('#rp_from').val().replace(/\//g,'-') + 'T00:00:00',zone);
|
|
||||||
var to = moment.tz($('#rp_to').val().replace(/\//g,'-') + 'T23:59:59',zone);
|
|
||||||
timerange = '&find[created_at][$gte]='+from.toISOString()+'&find[created_at][$lt]='+to.toISOString();
|
|
||||||
//console.log($('#rp_from').val(),$('#rp_to').val(),zone,timerange);
|
|
||||||
while (from <= to) {
|
|
||||||
if (daystoshow[from.format('YYYY-MM-DD')]) {
|
|
||||||
daystoshow[from.format('YYYY-MM-DD')]++;
|
|
||||||
} else {
|
|
||||||
daystoshow[from.format('YYYY-MM-DD')] = 1;
|
|
||||||
}
|
|
||||||
from.add(1, 'days');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log('Dayfilter: ',daystoshow);
|
|
||||||
foodfilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
//food filter
|
|
||||||
function foodfilter() {
|
|
||||||
if ($('#rp_enablefood').is(':checked')) {
|
|
||||||
matchesneeded++;
|
|
||||||
var _id = $('#rp_food').val();
|
|
||||||
if (_id) {
|
|
||||||
var treatmentData;
|
|
||||||
var tquery = '?find[boluscalc.foods._id]=' + _id + timerange;
|
|
||||||
$.ajax('/api/v1/treatments.json'+tquery, {
|
|
||||||
headers: client.headers()
|
|
||||||
, success: function (xhr) {
|
|
||||||
treatmentData = xhr.map(function (treatment) {
|
|
||||||
return moment.tz(treatment.created_at,zone).format('YYYY-MM-DD');
|
|
||||||
});
|
|
||||||
// unique it
|
|
||||||
treatmentData = $.grep(treatmentData, function(v, k){
|
|
||||||
return $.inArray(v ,treatmentData) === k;
|
|
||||||
});
|
|
||||||
treatmentData.sort(function(a, b) { return a > b; });
|
|
||||||
}
|
|
||||||
}).done(function () {
|
|
||||||
//console.log('Foodfilter: ',treatmentData);
|
|
||||||
for (var d=0; d<treatmentData.length; d++) {
|
|
||||||
if (daystoshow[treatmentData[d]]) {
|
|
||||||
daystoshow[treatmentData[d]]++;
|
|
||||||
} else {
|
|
||||||
daystoshow[treatmentData[d]] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notesfilter();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
notesfilter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//notes filter
|
|
||||||
function notesfilter() {
|
|
||||||
if ($('#rp_enablenotes').is(':checked')) {
|
|
||||||
matchesneeded++;
|
|
||||||
var notes = $('#rp_notes').val();
|
|
||||||
if (notes) {
|
|
||||||
var treatmentData;
|
|
||||||
var tquery = '?find[notes]=/' + notes + '/i';
|
|
||||||
$.ajax('/api/v1/treatments.json' + tquery + timerange, {
|
|
||||||
headers: client.headers()
|
|
||||||
, success: function (xhr) {
|
|
||||||
treatmentData = xhr.map(function (treatment) {
|
|
||||||
return moment.tz(treatment.created_at,zone).format('YYYY-MM-DD');
|
|
||||||
});
|
|
||||||
// unique it
|
|
||||||
treatmentData = $.grep(treatmentData, function(v, k){
|
|
||||||
return $.inArray(v ,treatmentData) === k;
|
|
||||||
});
|
|
||||||
treatmentData.sort(function(a, b) { return a > b; });
|
|
||||||
}
|
|
||||||
}).done(function () {
|
|
||||||
//console.log('Notesfilter: ',treatmentData);
|
|
||||||
for (var d=0; d<treatmentData.length; d++) {
|
|
||||||
if (daystoshow[treatmentData[d]]) {
|
|
||||||
daystoshow[treatmentData[d]]++;
|
|
||||||
} else {
|
|
||||||
daystoshow[treatmentData[d]] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eventtypefilter();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eventtypefilter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//event type filter
|
|
||||||
function eventtypefilter() {
|
|
||||||
if ($('#rp_enableeventtype').is(':checked')) {
|
|
||||||
matchesneeded++;
|
|
||||||
var eventtype = $('#rp_eventtype').val();
|
|
||||||
if (eventtype) {
|
|
||||||
var treatmentData;
|
|
||||||
var tquery = '?find[eventType]=/' + eventtype + '/i';
|
|
||||||
$.ajax('/api/v1/treatments.json' + tquery + timerange, {
|
|
||||||
headers: client.headers()
|
|
||||||
, success: function (xhr) {
|
|
||||||
treatmentData = xhr.map(function (treatment) {
|
|
||||||
return moment.tz(treatment.created_at,zone).format('YYYY-MM-DD');
|
|
||||||
});
|
|
||||||
// unique it
|
|
||||||
treatmentData = $.grep(treatmentData, function(v, k){
|
|
||||||
return $.inArray(v ,treatmentData) === k;
|
|
||||||
});
|
|
||||||
treatmentData.sort(function(a, b) { return a > b; });
|
|
||||||
}
|
|
||||||
}).done(function () {
|
|
||||||
//console.log('Eventtypefilter: ',treatmentData);
|
|
||||||
for (var d=0; d<treatmentData.length; d++) {
|
|
||||||
if (daystoshow[treatmentData[d]]) {
|
|
||||||
daystoshow[treatmentData[d]]++;
|
|
||||||
} else {
|
|
||||||
daystoshow[treatmentData[d]] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
daysfilter();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
daysfilter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function daysfilter() {
|
|
||||||
matchesneeded++;
|
|
||||||
Object.keys(daystoshow).forEach( function eachDay(d) {
|
|
||||||
var day = moment.tz(d,zone).day();
|
|
||||||
if (day===0 && $('#rp_su').is(':checked')) { daystoshow[d]++; }
|
|
||||||
if (day===1 && $('#rp_mo').is(':checked')) { daystoshow[d]++; }
|
|
||||||
if (day===2 && $('#rp_tu').is(':checked')) { daystoshow[d]++; }
|
|
||||||
if (day===3 && $('#rp_we').is(':checked')) { daystoshow[d]++; }
|
|
||||||
if (day===4 && $('#rp_th').is(':checked')) { daystoshow[d]++; }
|
|
||||||
if (day===5 && $('#rp_fr').is(':checked')) { daystoshow[d]++; }
|
|
||||||
if (day===6 && $('#rp_sa').is(':checked')) { daystoshow[d]++; }
|
|
||||||
});
|
|
||||||
countDays();
|
|
||||||
addPreviousDayTreatments();
|
|
||||||
display();
|
|
||||||
}
|
|
||||||
|
|
||||||
function display() {
|
|
||||||
var count = 0;
|
|
||||||
sorteddaystoshow = [];
|
|
||||||
$('#info').html('<b>'+translate('Loading')+' ...</b>');
|
|
||||||
for (var d in daystoshow) {
|
|
||||||
if (count < maxdays) {
|
|
||||||
$('#info').append($('<div id="info-' + d + '"></div>'));
|
|
||||||
count++;
|
|
||||||
loadData(d, options, dataLoadedCallback);
|
|
||||||
} else {
|
|
||||||
$('#info').append($('<div>'+d+' '+translate('not displayed')+'.</div>'));
|
|
||||||
delete daystoshow[d];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count===0) {
|
|
||||||
$('#info').html('<b>'+translate('Result is empty')+'</b>');
|
|
||||||
$('#rp_show').css('display','');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var dayscount = 0;
|
|
||||||
var loadeddays = 0;
|
|
||||||
|
|
||||||
function countDays() {
|
|
||||||
for (var d in daystoshow) {
|
|
||||||
if (daystoshow.hasOwnProperty(d)) {
|
|
||||||
if (daystoshow[d]===matchesneeded) {
|
|
||||||
if (dayscount < maxdays) {
|
|
||||||
dayscount++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete daystoshow[d];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log('Total: ', daystoshow, 'Matches needed: ', matchesneeded, 'Will be loaded: ', dayscount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addPreviousDayTreatments() {
|
|
||||||
for (var d in daystoshow) {
|
|
||||||
if (daystoshow.hasOwnProperty(d)) {
|
|
||||||
var day = moment.tz(d,zone);
|
|
||||||
var previous = day.subtract(1,'days');
|
|
||||||
var formated = previous.format('YYYY-MM-DD');
|
|
||||||
if (!daystoshow[formated]) {
|
|
||||||
daystoshow[formated] = { treatmentsonly: true};
|
|
||||||
console.log('Adding ' + formated + ' for loading treatments');
|
|
||||||
dayscount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log('Total: ', daystoshow, 'Matches needed: ', matchesneeded, 'Will be loaded: ', dayscount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dataLoadedCallback (day) {
|
|
||||||
loadeddays++;
|
|
||||||
if (!daystoshow[day].treatmentsonly) {
|
|
||||||
sorteddaystoshow.push(day);
|
|
||||||
}
|
|
||||||
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(dFrom, function loadProfileSwitchCallback() {
|
|
||||||
loadProfilesRange(dFrom, dTo, sorteddaystoshow.length, function loadProfilesCallback() {
|
|
||||||
$('#info > b').html('<b>' + translate('Rendering') + ' ...</b>');
|
|
||||||
window.setTimeout(function () {
|
|
||||||
showreports(options);
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#rp_show').css('display','none');
|
|
||||||
daystoshow = {};
|
|
||||||
|
|
||||||
datefilter();
|
|
||||||
return maybePrevent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showreports(options) {
|
|
||||||
// prepare some data used in more reports
|
|
||||||
datastorage.allstatsrecords = [];
|
|
||||||
datastorage.alldays = 0;
|
|
||||||
sorteddaystoshow.forEach(function eachDay(day) {
|
|
||||||
if (!daystoshow[day].treatmentsonly) {
|
|
||||||
datastorage.allstatsrecords = datastorage.allstatsrecords.concat(datastorage[day].statsrecords);
|
|
||||||
datastorage.alldays++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
options.maxInsulinValue = maxInsulinValue;
|
|
||||||
options.maxCarbsValue = maxCarbsValue;
|
|
||||||
options.maxDailyCarbsValue = maxDailyCarbsValue;
|
|
||||||
|
|
||||||
datastorage.treatments = [];
|
|
||||||
datastorage.devicestatus = [];
|
|
||||||
datastorage.combobolusTreatments = [];
|
|
||||||
datastorage.tempbasalTreatments = [];
|
|
||||||
Object.keys(daystoshow).forEach( function eachDay(day) {
|
|
||||||
datastorage.treatments = datastorage.treatments.concat(datastorage[day].treatments);
|
|
||||||
datastorage.devicestatus = datastorage.devicestatus.concat(datastorage[day].devicestatus);
|
|
||||||
datastorage.combobolusTreatments = datastorage.combobolusTreatments.concat(datastorage[day].combobolusTreatments);
|
|
||||||
datastorage.tempbasalTreatments = datastorage.tempbasalTreatments.concat(datastorage[day].tempbasalTreatments);
|
|
||||||
});
|
|
||||||
datastorage.tempbasalTreatments = Nightscout.client.ddata.processDurations(datastorage.tempbasalTreatments);
|
|
||||||
datastorage.treatments.sort(function sort(a, b) {return a.mills - b.mills; });
|
|
||||||
|
|
||||||
for (var d in daystoshow) {
|
|
||||||
if (daystoshow.hasOwnProperty(d)) {
|
|
||||||
if (daystoshow[d].treatmentsonly) {
|
|
||||||
delete daystoshow[d];
|
|
||||||
delete datastorage[d];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
report_plugins.eachPlugin(function (plugin) {
|
|
||||||
// jquery plot doesn't draw to hidden div
|
|
||||||
$('#'+plugin.name+'-placeholder').css('display','');
|
|
||||||
|
|
||||||
console.log('Drawing ',plugin.name);
|
|
||||||
|
|
||||||
var skipRender = false;
|
|
||||||
|
|
||||||
if (plugin.name == 'daytoday' && ! $('#daytoday').hasClass('selected')) skipRender = true;
|
|
||||||
if (plugin.name == 'treatments' && ! $('#treatments').hasClass('selected')) skipRender = true;
|
|
||||||
if (plugin.name == 'weektoweek' && ! $('#weektoweek').hasClass('selected')) skipRender = true;
|
|
||||||
if (plugin.name == 'loopalyzer' && ! $('#loopalyzer').hasClass('selected')) skipRender = true;
|
|
||||||
|
|
||||||
if (skipRender) {
|
|
||||||
console.log('Skipping ',plugin.name);
|
|
||||||
} else {
|
|
||||||
plugin.report(datastorage,sorteddaystoshow,options);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$('#'+plugin.name).hasClass('selected')) {
|
|
||||||
$('#'+plugin.name+'-placeholder').css('display','none');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#info').html('');
|
|
||||||
$('#rp_show').css('display','');
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDataRange(event,days) {
|
|
||||||
$('#rp_to').val(moment().format('YYYY-MM-DD'));
|
|
||||||
$('#rp_from').val(moment().add(-days+1, 'days').format('YYYY-MM-DD'));
|
|
||||||
return maybePrevent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function switchreport_handler(event) {
|
|
||||||
var id = $(this).attr('id');
|
|
||||||
|
|
||||||
$('.menutab').removeClass('selected');
|
|
||||||
$('#'+id).addClass('selected');
|
|
||||||
|
|
||||||
$('.tabplaceholder').css('display','none');
|
|
||||||
$('#'+id+'-placeholder').css('display','');
|
|
||||||
return maybePrevent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadData(day, options, callback) {
|
|
||||||
// check for loaded data
|
|
||||||
if ((options.openAps || options.predicted || options.iob || options.cob) && datastorage[day] && !datastorage[day].devicestatus.length) {
|
|
||||||
// OpenAPS requested but data not loaded. Load anyway ...
|
|
||||||
} else if (datastorage[day] && day !== moment().format('YYYY-MM-DD')) {
|
|
||||||
callback(day);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// patientData = [actual, predicted, mbg, treatment, cal, devicestatusData];
|
|
||||||
var data = {};
|
|
||||||
var cgmData = []
|
|
||||||
, mbgData = []
|
|
||||||
, treatmentData = []
|
|
||||||
, calData = []
|
|
||||||
;
|
|
||||||
var from;
|
|
||||||
if (client.sbx.data.profile.getTimezone()) {
|
|
||||||
from = moment(day).tz(client.sbx.data.profile.getTimezone()).startOf('day').format('x');
|
|
||||||
} else {
|
|
||||||
from = moment(day).startOf('day').format('x');
|
|
||||||
}
|
|
||||||
from = parseInt(from);
|
|
||||||
var to = from + 1000 * 60 * 60 * 24;
|
|
||||||
|
|
||||||
function loadCGMData() {
|
|
||||||
if (daystoshow[day].treatmentsonly) {
|
|
||||||
data.sgv = [];
|
|
||||||
data.mbg = [];
|
|
||||||
data.cal = [];
|
|
||||||
return $.Deferred().resolve();
|
|
||||||
}
|
|
||||||
$('#info-' + day).html('<b>'+translate('Loading CGM data of')+' '+day+' ...</b>');
|
|
||||||
var query = '?find[date][$gte]='+from+'&find[date][$lt]='+to+'&count=10000';
|
|
||||||
return $.ajax('/api/v1/entries.json'+query, {
|
|
||||||
headers: client.headers()
|
|
||||||
, success: function (xhr) {
|
|
||||||
xhr.forEach(function (element) {
|
|
||||||
if (element) {
|
|
||||||
if (element.mbg) {
|
|
||||||
mbgData.push({
|
|
||||||
y: element.mbg
|
|
||||||
, mills: element.date
|
|
||||||
, d: element.dateString
|
|
||||||
, device: element.device
|
|
||||||
});
|
|
||||||
} else if (element.sgv) {
|
|
||||||
cgmData.push({
|
|
||||||
y: element.sgv
|
|
||||||
, mills: element.date
|
|
||||||
, d: element.dateString
|
|
||||||
, device: element.device
|
|
||||||
, filtered: element.filtered
|
|
||||||
, unfiltered: element.unfiltered
|
|
||||||
, noise: element.noise
|
|
||||||
, rssi: element.rssi
|
|
||||||
, sgv: element.sgv
|
|
||||||
});
|
|
||||||
} else if (element.type === 'cal') {
|
|
||||||
calData.push({
|
|
||||||
mills: element.date + 1
|
|
||||||
, d: element.dateString
|
|
||||||
, scale: element.scale
|
|
||||||
, intercept: element.intercept
|
|
||||||
, slope: element.slope
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// sometimes cgm contains duplicates. uniq it.
|
|
||||||
data.sgv = cgmData.slice();
|
|
||||||
data.sgv.sort(function(a, b) { return a.mills - b.mills; });
|
|
||||||
var lastDate = 0;
|
|
||||||
data.sgv = data.sgv.filter(function(d) {
|
|
||||||
var ok = (lastDate + ONE_MIN_IN_MS) <= d.mills;
|
|
||||||
lastDate = d.mills;
|
|
||||||
if (!ok) { console.log("itm",JSON.stringify(d)); }
|
|
||||||
return ok;
|
|
||||||
});
|
|
||||||
data.mbg = mbgData.slice();
|
|
||||||
data.mbg.sort(function(a, b) { return a.mills - b.mills; });
|
|
||||||
data.cal = calData.slice();
|
|
||||||
data.cal.sort(function(a, b) { return a.mills - b.mills; });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadTreatmentData() {
|
|
||||||
if (!datastorage.profileSwitchTreatments)
|
|
||||||
datastorage.profileSwitchTreatments = [];
|
|
||||||
$('#info-' + day).html('<b>'+translate('Loading treatments data of')+' '+day+' ...</b>');
|
|
||||||
var tquery = '?find[created_at][$gte]='+new Date(from).toISOString()+'&find[created_at][$lt]='+new Date(to).toISOString()+'&count=1000';
|
|
||||||
return $.ajax('/api/v1/treatments.json'+tquery, {
|
|
||||||
headers: client.headers()
|
|
||||||
, cache: false
|
|
||||||
, success: function (xhr) {
|
|
||||||
treatmentData = xhr.map(function (treatment) {
|
|
||||||
var timestamp = new Date(treatment.timestamp || treatment.created_at);
|
|
||||||
treatment.mills = timestamp.getTime();
|
|
||||||
return treatment;
|
|
||||||
});
|
|
||||||
data.treatments = treatmentData.slice();
|
|
||||||
data.treatments.sort(function(a, b) { return a.mills - b.mills; });
|
|
||||||
// filter 'Combo Bolus' events
|
|
||||||
data.combobolusTreatments = data.treatments.filter( function filterComboBoluses(t) {
|
|
||||||
return t.eventType === 'Combo Bolus';
|
|
||||||
});
|
|
||||||
// filter temp basal treatments
|
|
||||||
data.tempbasalTreatments = data.treatments.filter(function filterTempBasals(t) {
|
|
||||||
return t.eventType === 'Temp Basal';
|
|
||||||
});
|
|
||||||
// filter profile switch treatments
|
|
||||||
var profileSwitch = data.treatments.filter(function filterProfileSwitch(t) {
|
|
||||||
return t.eventType === 'Profile Switch';
|
|
||||||
});
|
|
||||||
datastorage.profileSwitchTreatments = datastorage.profileSwitchTreatments.concat(profileSwitch);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadDevicestatusData() {
|
|
||||||
if (daystoshow[day].treatmentsonly) {
|
|
||||||
data.devicestatus = [];
|
|
||||||
return $.Deferred().resolve();
|
|
||||||
}
|
|
||||||
if (options.iob || options.cob || options.openAps || options.predicted) {
|
|
||||||
$('#info-' + day).html('<b>'+translate('Loading device status data of')+' '+day+' ...</b>');
|
|
||||||
var tquery = '?find[created_at][$gte]=' + new Date(from).toISOString() + '&find[created_at][$lt]=' + new Date(to).toISOString() + '&count=10000';
|
|
||||||
return $.ajax('/api/v1/devicestatus.json'+tquery, {
|
|
||||||
headers: client.headers()
|
|
||||||
, success: function (xhr) {
|
|
||||||
data.devicestatus = xhr.map(function (devicestatus) {
|
|
||||||
devicestatus.mills = new Date(devicestatus.timestamp || devicestatus.created_at).getTime();
|
|
||||||
return devicestatus;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data.devicestatus = [];
|
|
||||||
return $.Deferred().resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$.when(loadCGMData(), loadTreatmentData(), loadDevicestatusData()).done(function () {
|
|
||||||
$('#info-' + day).html('<b>'+translate('Processing data of')+' '+day+' ...</b>');
|
|
||||||
processData(data, day, options, callback);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadProfileSwitch (from, callback) {
|
|
||||||
$('#info > b').html('<b>'+translate('Loading profile switch data') + ' ...</b>');
|
|
||||||
var tquery = '?find[eventType]=Profile Switch' + '&find[created_at][$lte]=' + new Date(from).toISOString() + '&count=1';
|
|
||||||
$.ajax('/api/v1/treatments.json'+tquery, {
|
|
||||||
headers: client.headers()
|
|
||||||
, success: function (xhr) {
|
|
||||||
var treatmentData = xhr.map(function (treatment) {
|
|
||||||
var timestamp = new Date(treatment.timestamp || treatment.created_at);
|
|
||||||
treatment.mills = timestamp.getTime();
|
|
||||||
return treatment;
|
|
||||||
});
|
|
||||||
if (!datastorage.profileSwitchTreatments)
|
|
||||||
datastorage.profileSwitchTreatments = [];
|
|
||||||
datastorage.profileSwitchTreatments = datastorage.profileSwitchTreatments.concat(treatmentData);
|
|
||||||
datastorage.profileSwitchTreatments.sort(function(a, b) { return a.mills - b.mills; });
|
|
||||||
}
|
|
||||||
}).done(function () {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadProfiles(callback) {
|
|
||||||
$('#info > b').html('<b>'+translate('Loading profiles') + ' ...</b>');
|
|
||||||
$.ajax('/api/v1/profile.json', {
|
|
||||||
headers: client.headers()
|
|
||||||
, success: function (records) {
|
|
||||||
datastorage.profiles = records;
|
|
||||||
}
|
|
||||||
, error: function () {
|
|
||||||
datastorage.profiles = [];
|
|
||||||
}
|
|
||||||
}).done(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadProfilesRange (dateFrom, dateTo, dayCount, callback) {
|
|
||||||
$('#info > b').html('<b>' + translate('Loading profile range') + ' ...</b>');
|
|
||||||
|
|
||||||
$.when(
|
|
||||||
loadProfilesRangeCore(dateFrom, dateTo, dayCount),
|
|
||||||
loadProfilesRangePrevious(dateFrom),
|
|
||||||
loadProfilesRangeNext(dateTo)
|
|
||||||
)
|
|
||||||
.done(callback)
|
|
||||||
.fail(function () {
|
|
||||||
datastorage.profiles = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadProfilesRangeCore (dateFrom, dateTo, dayCount) {
|
|
||||||
$('#info > b').html('<b>' + translate('Loading core profiles') + ' ...</b>');
|
|
||||||
|
|
||||||
//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=1000';
|
|
||||||
|
|
||||||
return $.ajax('/api/v1/profiles' + tquery, {
|
|
||||||
headers: client.headers(),
|
|
||||||
async: false,
|
|
||||||
success: function (records) {
|
|
||||||
datastorage.profiles = records;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadProfilesRangePrevious (dateFrom) {
|
|
||||||
$('#info > b').html('<b>' + translate('Loading previous profile') + ' ...</b>');
|
|
||||||
|
|
||||||
//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('<b>' + translate('Loading next profile') + ' ...</b>');
|
|
||||||
|
|
||||||
//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) {
|
|
||||||
datastorage[day] = data;
|
|
||||||
$('#info-' + day).html('');
|
|
||||||
callback(day);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// treatments
|
|
||||||
data.dailyCarbs = 0;
|
|
||||||
data.dailyProtein = 0;
|
|
||||||
data.dailyFat = 0;
|
|
||||||
|
|
||||||
data.treatments.forEach(function (d) {
|
|
||||||
if (parseFloat(d.insulin) > maxInsulinValue) {
|
|
||||||
maxInsulinValue = parseFloat(d.insulin);
|
|
||||||
}
|
|
||||||
if (parseFloat(d.carbs) > maxCarbsValue) {
|
|
||||||
maxCarbsValue = parseFloat(d.carbs);
|
|
||||||
}
|
|
||||||
if (d.carbs) {
|
|
||||||
data.dailyCarbs += Number(d.carbs);
|
|
||||||
}
|
|
||||||
if (d.protein) {
|
|
||||||
data.dailyProtein += Number(d.protein);
|
|
||||||
}
|
|
||||||
if (d.fat) {
|
|
||||||
data.dailyFat += Number(d.fat);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (data.dailyCarbs > maxDailyCarbsValue) {
|
|
||||||
maxDailyCarbsValue = data.dailyCarbs;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cal = data.cal[data.cal.length-1];
|
|
||||||
var temp1 = [ ];
|
|
||||||
var rawbg = client.rawbg;
|
|
||||||
if (cal) {
|
|
||||||
temp1 = data.sgv.map(function (entry) {
|
|
||||||
entry.mgdl = entry.y; // value names changed from enchilada
|
|
||||||
var rawBg = rawbg.calc(entry, cal);
|
|
||||||
return { mills: entry.mills, date: new Date(entry.mills - 2 * 1000), y: rawBg, sgv: client.utils.scaleMgdl(rawBg), color: 'gray', type: 'rawbg', filtered: entry.filtered, unfiltered: entry.unfiltered };
|
|
||||||
}).filter(function(entry) { return entry.y > 0});
|
|
||||||
}
|
|
||||||
var temp2 = data.sgv.map(function (obj) {
|
|
||||||
return { mills: obj.mills, date: new Date(obj.mills), y: obj.y, sgv: client.utils.scaleMgdl(obj.y), color: sgvToColor(client.utils.scaleMgdl(obj.y),options), type: 'sgv', noise: obj.noise, filtered: obj.filtered, unfiltered: obj.unfiltered};
|
|
||||||
});
|
|
||||||
data.sgv = [].concat(temp1, temp2);
|
|
||||||
|
|
||||||
//Add MBG's also, pretend they are SGV's
|
|
||||||
data.sgv = data.sgv.concat(data.mbg.map(function (obj) { return { date: new Date(obj.mills), y: obj.y, sgv: client.utils.scaleMgdl(obj.y), color: 'red', type: 'mbg', device: obj.device } }));
|
|
||||||
|
|
||||||
// make sure data range will be exactly 24h
|
|
||||||
var from;
|
|
||||||
if (client.sbx.data.profile.getTimezone()) {
|
|
||||||
from = moment(day).tz(client.sbx.data.profile.getTimezone()).startOf('day').toDate();
|
|
||||||
} else {
|
|
||||||
from = moment(day).startOf('day').toDate();
|
|
||||||
}
|
|
||||||
var to = new Date(from.getTime() + 1000 * 60 * 60 * 24);
|
|
||||||
data.sgv.push({ date: from, y: 40, sgv: 40, color: 'transparent', type: 'rawbg'});
|
|
||||||
data.sgv.push({ date: to, y: 40, sgv: 40, color: 'transparent', type: 'rawbg'});
|
|
||||||
|
|
||||||
// clear error data. we don't need it to display them
|
|
||||||
data.sgv = data.sgv.filter(function (d) {
|
|
||||||
if (d.y < 39) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
data.sgv = data.sgv.map(function eachSgv (sgv) {
|
|
||||||
var status = _.find(data.devicestatus, function (d) {
|
|
||||||
return d.mills >= sgv.mills && d.mills < sgv.mills + 5 * 60 * 1000;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (status && status.openaps) {
|
|
||||||
sgv.openaps = status.openaps;
|
|
||||||
}
|
|
||||||
return sgv;
|
|
||||||
});
|
|
||||||
|
|
||||||
// for other reports
|
|
||||||
data.statsrecords = data.sgv.filter(function(r) {
|
|
||||||
if (r.type) {
|
|
||||||
return r.type === 'sgv';
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}).map(function (r) {
|
|
||||||
var ret = {};
|
|
||||||
ret.sgv = parseFloat(r.sgv);
|
|
||||||
ret.bgValue = parseInt(r.y);
|
|
||||||
ret.displayTime = r.date;
|
|
||||||
return ret;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
datastorage[day] = data;
|
|
||||||
$('#info-' + day).html('');
|
|
||||||
callback(day);
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybePrevent(event) {
|
|
||||||
if (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|||||||
Reference in New Issue
Block a user