total.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. var tz = require('moment-timezone');
  2. var calcMealCOB = require('../determine-basal/cob');
  3. function round(value, digits) {
  4. if (! digits) { digits = 0; }
  5. var scale = Math.pow(10, digits);
  6. return Math.round(value * scale) / scale;
  7. }
  8. function recentCarbs(opts, time) {
  9. var treatments = opts.treatments;
  10. var profile_data = opts.profile;
  11. if (typeof(opts.glucose) !== 'undefined') {
  12. var glucose_data = opts.glucose;
  13. }
  14. var carbs = 0;
  15. var nsCarbs = 0;
  16. var bwCarbs = 0;
  17. var journalCarbs = 0;
  18. var bwFound = false;
  19. var mealCarbTime = time.getTime();
  20. var lastCarbTime = 0;
  21. if (!treatments) return {};
  22. //console.error(glucose_data);
  23. var iob_inputs = {
  24. profile: profile_data
  25. , history: opts.pumphistory
  26. };
  27. var COB_inputs = {
  28. glucose_data: glucose_data
  29. , iob_inputs: iob_inputs
  30. , basalprofile: opts.basalprofile
  31. , mealTime: mealCarbTime
  32. };
  33. var mealCOB = 0;
  34. // this sorts the treatments collection in order.
  35. treatments.sort(function (a, b) {
  36. var aDate = new Date(tz(a.timestamp));
  37. var bDate = new Date(tz(b.timestamp));
  38. //console.error(aDate);
  39. return bDate.getTime() - aDate.getTime();
  40. });
  41. var carbsToRemove = 0;
  42. var nsCarbsToRemove = 0;
  43. var bwCarbsToRemove = 0;
  44. var journalCarbsToRemove = 0;
  45. var maxMealAbsorptionTime = 6;
  46. if (typeof(profile_data.maxMealAbsorptionTime) === 'number' && ! isNaN(profile_data.maxMealAbsorptionTime)) {
  47. maxMealAbsorptionTime = profile_data.maxMealAbsorptionTime;
  48. } else {
  49. console.error("Bad profile.maxMealAbsorptionTime:",profile_data.maxMealAbsorptionTime);
  50. }
  51. treatments.forEach(function(treatment) {
  52. var now = time.getTime();
  53. // consider carbs from up to the meal preference maxMealAbsorptionTime hours ago in calculating COB
  54. var carbWindow = now - maxMealAbsorptionTime * 60*60*1000;
  55. var treatmentDate = new Date(tz(treatment.timestamp));
  56. var treatmentTime = treatmentDate.getTime();
  57. if (treatmentTime > carbWindow && treatmentTime <= now) {
  58. if (treatment.carbs >= 1) {
  59. if (treatment.nsCarbs >= 1) {
  60. nsCarbs += parseFloat(treatment.nsCarbs);
  61. } else if (treatment.bwCarbs >= 1) {
  62. bwCarbs += parseFloat(treatment.bwCarbs);
  63. bwFound = true;
  64. } else if (treatment.journalCarbs >= 1) {
  65. journalCarbs += parseFloat(treatment.journalCarbs);
  66. } else {
  67. console.error("Treatment carbs unclassified:",treatment);
  68. }
  69. //console.error(treatment.carbs, maxCarbs, treatmentDate);
  70. carbs += parseFloat(treatment.carbs);
  71. COB_inputs.mealTime = treatmentTime;
  72. lastCarbTime = Math.max(lastCarbTime,treatmentTime);
  73. var myCarbsAbsorbed = calcMealCOB(COB_inputs).carbsAbsorbed;
  74. var myMealCOB = Math.max(0, carbs - myCarbsAbsorbed);
  75. if (typeof(myMealCOB) === 'number' && ! isNaN(myMealCOB)) {
  76. mealCOB = Math.max(mealCOB, myMealCOB);
  77. } else {
  78. console.error("Bad myMealCOB:",myMealCOB, "mealCOB:",mealCOB, "carbs:",carbs,"myCarbsAbsorbed:",myCarbsAbsorbed);
  79. }
  80. if (myMealCOB < mealCOB) {
  81. carbsToRemove += parseFloat(treatment.carbs);
  82. if (treatment.nsCarbs >= 1) {
  83. nsCarbsToRemove += parseFloat(treatment.nsCarbs);
  84. } else if (treatment.bwCarbs >= 1) {
  85. bwCarbsToRemove += parseFloat(treatment.bwCarbs);
  86. } else if (treatment.journalCarbs >= 1) {
  87. journalCarbsToRemove += parseFloat(treatment.journalCarbs);
  88. }
  89. } else {
  90. carbsToRemove = 0;
  91. nsCarbsToRemove = 0;
  92. bwCarbsToRemove = 0;
  93. }
  94. //console.error(carbs, carbsToRemove);
  95. //console.error("COB:",mealCOB);
  96. }
  97. }
  98. });
  99. // only include carbs actually used in calculating COB
  100. carbs -= carbsToRemove;
  101. nsCarbs -= nsCarbsToRemove;
  102. bwCarbs -= bwCarbsToRemove;
  103. journalCarbs -= journalCarbsToRemove;
  104. // calculate the current deviation and steepest deviation downslope over the last hour
  105. COB_inputs.ciTime = time.getTime();
  106. // set mealTime to 6h ago for Deviation calculations
  107. COB_inputs.mealTime = time.getTime() - maxMealAbsorptionTime * 60 * 60 * 1000;
  108. var c = calcMealCOB(COB_inputs);
  109. //console.error(c.currentDeviation, c.slopeFromMaxDeviation);
  110. // set a hard upper limit on COB to mitigate impact of erroneous or malicious carb entry
  111. if (typeof(profile_data.maxCOB) === 'number' && ! isNaN(profile_data.maxCOB)) {
  112. mealCOB = Math.min( profile_data.maxCOB, mealCOB );
  113. console.error("mealCOB: " + round(mealCOB,1) + " with maxCOB " + profile_data.maxCOB + "g and maxMealAbsorptionTime " + maxMealAbsorptionTime + "hrs.");
  114. } else {
  115. console.error("Bad profile.maxCOB:",profile_data.maxCOB);
  116. }
  117. // if currentDeviation is null or maxDeviation is 0, set mealCOB to 0 for zombie-carb safety
  118. if (typeof(c.currentDeviation) === 'undefined' || c.currentDeviation === null) {
  119. console.error("");
  120. console.error("Warning: setting mealCOB to 0 because currentDeviation is null/undefined");
  121. mealCOB = 0;
  122. }
  123. if (typeof(c.maxDeviation) === 'undefined' || c.maxDeviation === null) {
  124. console.error("");
  125. console.error("Warning: setting mealCOB to 0 because maxDeviation is 0 or undefined");
  126. mealCOB = 0;
  127. }
  128. return {
  129. carbs: Math.round( carbs * 1000 ) / 1000
  130. , nsCarbs: Math.round( nsCarbs * 1000 ) / 1000
  131. , bwCarbs: Math.round( bwCarbs * 1000 ) / 1000
  132. , journalCarbs: Math.round( journalCarbs * 1000 ) / 1000
  133. , mealCOB: Math.round( mealCOB )
  134. , currentDeviation: Math.round( c.currentDeviation * 100 ) / 100
  135. , maxDeviation: Math.round( c.maxDeviation * 100 ) / 100
  136. , minDeviation: Math.round( c.minDeviation * 100 ) / 100
  137. , slopeFromMaxDeviation: Math.round( c.slopeFromMaxDeviation * 1000 ) / 1000
  138. , slopeFromMinDeviation: Math.round( c.slopeFromMinDeviation * 1000 ) / 1000
  139. , allDeviations: c.allDeviations
  140. , lastCarbTime: lastCarbTime
  141. , bwFound: bwFound
  142. };
  143. }
  144. exports = module.exports = recentCarbs;