Skip to content

Commit

Permalink
reimplement #3179 (Predictions support to Reports)
Browse files Browse the repository at this point in the history
  • Loading branch information
PieterGit committed Feb 3, 2019
1 parent 48dbb80 commit 7b09c9e
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 52 deletions.
124 changes: 124 additions & 0 deletions lib/report_plugins/daytoday.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ daytoday.html = function html(client) {
+ '<input type="checkbox" id="rp_optionsraw"><span style="color:gray;opacity:1">'+translate('Raw')+'</span>'
+ '<input type="checkbox" id="rp_optionsiob"><span style="color:blue;opacity:0.5">'+translate('IOB')+'</span>'
+ '<input type="checkbox" id="rp_optionscob"><span style="color:red;opacity:0.5">'+translate('COB')+'</span>'
+ '<input type="checkbox" id="rp_optionspredicted"><span style="color:sienna;opacity:0.5">'+translate('Predictions')+'</span>'
+ '<input type="checkbox" id="rp_optionsopenaps"><span style="color:sienna;opacity:0.5">'+translate('OpenAPS')+'</span>'
+ '<input type="checkbox" id="rp_optionsdistribution"><span style="color:blue;opacity:0.5">'+translate('Insulin distribution')+'</span>'
+ '&nbsp;'+translate('Size')
Expand All @@ -39,13 +40,27 @@ daytoday.html = function html(client) {
+ ' <option x="1000" y="300" selected>1000x300px</option>'
+ ' <option x="1200" y="400">1200x400px</option>'
+ ' <option x="1550" y="600">1550x600px</option>'
+ ' <option x="2400" y="800">2400x800px</option>'
+ '</select>'
+ '<br>'
+ translate('Scale') + ': '
+ '<input type="radio" name="rp_scale" id="rp_linear" checked>'
+ translate('Linear')
+ '<input type="radio" name="rp_scale" id="rp_log">'
+ translate('Logarithmic')
+ '<div id="rp_predictedSettings" style="display:none">'
+ translate('Truncate predictions: ')
+ '<input type="checkbox" id="rp_optionsPredictedTruncate" checked>'
+ '<br>'
+ translate('Predictions offset') + ': '
+ '<b><label id="rp_predictedOffset"></label> minutes</b>'
+ '&nbsp;&nbsp;&nbsp;&nbsp;'
+ '<input type="button" onclick="predictMoreBackward();" value="' + translate('-30 min')+'">'
+ '<input type="button" onclick="predictBackward();" value="' + translate('-5 min')+'">'
+ '<input type="button" onclick="predictResetToZero();" value="' + translate('Zero')+'">'
+ '<input type="button" onclick="predictForward();" value="' + translate('+5 min')+'">'
+ '<input type="button" onclick="predictMoreForward();" value="' + translate('+30 min')+'">'
+ '</div>'
+ '<br>'
+ '<div id="daytodaycharts">'
+ '</div>'
Expand Down Expand Up @@ -292,6 +307,115 @@ daytoday.report = function report_daytoday(datastorage,sorteddaystoshow,options)
return sel;
}

// PREDICTIONS START
//
function preparePredictedData() {

var treatmentsTimestamps = []; // Only timestamps for (carbs and bolus insulin) treatments will be captured in this array
treatmentsTimestamps.push(dataRange[0]); // Create a fake timestamp at midnight so we can show predictions during night
for (var i in data.treatments) {
var treatment = data.treatments[i];
if (undefined != treatment.carbs && null != treatment.carbs && treatment.carbs > 0) {
if (treatment.timestamp)
treatmentsTimestamps.push(treatment.timestamp);
else if (treatment.created_at)
treatmentsTimestamps.push(treatment.created_at);
}
if (undefined != treatment.insulin && null != treatment.insulin && treatment.insulin > 0) {
if (treatment.timestamp)
treatmentsTimestamps.push(treatment.timestamp);
else if (treatment.created_at)
treatmentsTimestamps.push(treatment.created_at);
}
}

var predictions = [];
if (data && data.devicestatus) {
for (var i = data.devicestatus.length - 1; i >= 0; i--) {
if (data.devicestatus[i].loop && data.devicestatus[i].loop.predicted) {
predictions.push(data.devicestatus[i].loop.predicted);
} else if (data.devicestatus[i].openaps && data.devicestatus[i].openaps.suggested && data.devicestatus[i].openaps.suggested.predBGs) {
var entry = {};
entry.startDate = data.devicestatus[i].openaps.suggested.timestamp;
// For OpenAPS/AndroidAPS we fall back from COB if present, to UAM, then IOB
if (data.devicestatus[i].openaps.suggested.predBGs.COB) {
entry.values = data.devicestatus[i].openaps.suggested.predBGs.COB;
} else if (data.devicestatus[i].openaps.suggested.predBGs.UAM) {
entry.values = data.devicestatus[i].openaps.suggested.predBGs.UAM;
} else entry.values = data.devicestatus[i].openaps.suggested.predBGs.IOB;
predictions.push(entry);
}
}
}

var p = [];
if (predictions.length > 0 && treatmentsTimestamps.length > 0) {

// Iterate over all treatments, find the predictions for each and add them to the predicted array p
for (var treatmentsIndex = 0; treatmentsIndex < treatmentsTimestamps.length; treatmentsIndex++) {
var timestamp = treatmentsTimestamps[treatmentsIndex];
var predictedIndex = findPredicted(predictions, timestamp, predictedOffset); // Find predictions offset before or after timestamp

if (predictedIndex != null) {
var entry = predictions[predictedIndex]; // Start entry
var d = moment(entry.startDate);
var end = moment().endOf('day');
if (options.predictedTruncate) {
if (predictedOffset >= 0) {
// If we are looking forward we want to stop at the next treatment
if (treatmentsIndex < treatmentsTimestamps.length - 1) {
end = moment(treatmentsTimestamps[treatmentsIndex + 1]);
}
} else {
// If we are looking back, then we want to stop at "this" treatment
end = moment(treatmentsTimestamps[treatmentsIndex]);
}
}
for (var entryIndex in entry.values) {
if (!d.isAfter(end)) {
var value = {};
value.sgv = client.utils.scaleMgdl(entry.values[entryIndex]);
value.date = d.toDate();
value.color = 'purple';
p.push(value);
d.add(5, 'minutes');
}
}
}
}
}
return p;
}

/* Find the earliest new predicted instance that has a timestamp equal to or larger than timestamp */
/* (so if we have bolused or eaten we want to find the prediction that Loop has estimated just after that) */
/* Returns the index into the predictions array that is the predicted we are looking for */
function findPredicted(predictions, timestamp, offset) {
var ts = moment(timestamp).add(offset, 'minutes');
var predicted = null;
if (offset && offset < 0) { // If offset is negative, start searching from first prediction going forward
for (var i = 0; i < predictions.length; i++) {
if (predictions[i] && predictions[i].startDate && moment(predictions[i].startDate) <= ts) {
predicted = i;
}
}
} else { // If offset is positive or zero, start searching from last prediction going backward
for (var i = predictions.length - 1; i > 0; i--) {
if (predictions[i] && predictions[i].startDate && moment(predictions[i].startDate) >= ts) {
predicted = i;
}
}
}
return predicted;
}
//
// PREDICTIONS ENDS


// bind up the context chart data to an array of circles
var contextData = (options.predicted ? data.sgv.concat(preparePredictedData()) : data.sgv);
var contextCircles = context.selectAll('circle').data(contextData);

// if new circle then just display
prepareContextCircles(contextCircles.enter().append('circle'));

Expand Down

0 comments on commit 7b09c9e

Please sign in to comment.