total.js 5.7 KB

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