Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions summary/types/glucose.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,10 @@ func (r *Range) UpdateTotal(record Glucose) {

// CombineVariance Implemented using https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm
func (r *Range) CombineVariance(new *Range) float64 {
// Exit early for No-Op case
if r.Variance == 0 && new.Variance == 0 {
return 0
}

// Return new if existing is 0
if r.Variance == 0 {
return new.Variance
}
// [BACK-4044] Only return early if either range has 0 records. Otherwise
// proceed with variance calculation even if one range has no variance in
// order to properly handle the case where multiple ranges only have one
// record each.

// if we have no values in either bucket, this will result in NaN, and cant be added anyway, return what we started with
if r.Records == 0 || new.Records == 0 {
Expand Down
117 changes: 116 additions & 1 deletion summary/types/glucose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,10 @@ var _ = Describe("Glucose", func() {
// expect percent cleared, we don't handle percent on add
Expect(firstRange.Percent).To(BeZero())
})
})

It("range.Add without minutes", func() {
Describe("range.Add without minutes", func() {
It("multiple samples per range", func() {
firstRange := Range{
Glucose: 1,
Minutes: 0,
Expand Down Expand Up @@ -384,6 +386,46 @@ var _ = Describe("Glucose", func() {
// expect percent cleared, we don't handle percent on add
Expect(firstRange.Percent).To(BeZero())
})

It("one sample per range", func() {
// Test to cover [BACK-4044]
ranges := []Range{
{
Glucose: 10,
Minutes: 0,
Records: 1,
},
{
Glucose: 15,
Minutes: 0,
Records: 1,
},
{
Glucose: 8,
Minutes: 0,
Records: 1,
},
{
Glucose: 11,
Minutes: 0,
Records: 1,
},
}
rootRange := Range{}
vals := []float64{}
var runningTotal float64
for i, rng := range ranges {
rootRange.Add(&rng)
runningTotal += rng.Glucose
mean := runningTotal / float64(i+1)
vals = append(vals, rng.Glucose)
variance := CalculateVariance(vals, mean)
Expect(rootRange.Variance).To(Equal(variance))
if i > 0 {
Expect(rootRange.Variance).To(BeNumerically(">", math.SmallestNonzeroFloat64))
}
}
})
})

Context("Ranges", func() {
Expand Down Expand Up @@ -1056,6 +1098,79 @@ var _ = Describe("Glucose", func() {
Expect(period.Max).To(Equal(InTargetBloodGlucose))
})

It("Add four single record buckets to a period", func() {
datumTime := bucketTime.Add(5 * time.Minute)
period = GlucosePeriod{}

bucketOne := NewBucket[*GlucoseBucket](userId, bucketTime, SummaryTypeBGM)
err = bucketOne.Update(NewSelfMonitoredGlucoseWithValue(datumTime, 5.0))
Expect(err).ToNot(HaveOccurred())

bucketTwo := NewBucket[*GlucoseBucket](userId, bucketTime.Add(time.Hour), SummaryTypeBGM)
err = bucketTwo.Update(NewSelfMonitoredGlucoseWithValue(datumTime.Add(time.Hour), 5.0))
Expect(err).ToNot(HaveOccurred())

bucketThree := NewBucket[*GlucoseBucket](userId, bucketTime.Add(26*time.Hour), SummaryTypeBGM)
err = bucketThree.Update(NewSelfMonitoredGlucoseWithValue(datumTime.Add(26*time.Hour), 11.0))
Expect(err).ToNot(HaveOccurred())

bucketFour := NewBucket[*GlucoseBucket](userId, bucketTime.Add(27*time.Hour), SummaryTypeBGM)
err = bucketFour.Update(NewSelfMonitoredGlucoseWithValue(datumTime.Add(27*time.Hour), 11.0))
Expect(err).ToNot(HaveOccurred())

err = period.Update(bucketOne)
Expect(err).ToNot(HaveOccurred())
Expect(period.Target.Records).To(Equal(1))
Expect(period.HoursWithData).To(Equal(1))
Expect(period.DaysWithData).To(Equal(1))
Expect(period.Min).To(Equal(5.0))
Expect(period.Max).To(Equal(5.0))

err = period.Update(bucketTwo)
Expect(err).ToNot(HaveOccurred())
Expect(period.Target.Records).To(Equal(2))
Expect(period.HoursWithData).To(Equal(2))
Expect(period.DaysWithData).To(Equal(1))
Expect(period.Min).To(Equal(5.0))
Expect(period.Max).To(Equal(5.0))

err = period.Update(bucketThree)
Expect(err).ToNot(HaveOccurred())
Expect(period.Target.Records).To(Equal(2))
Expect(period.HoursWithData).To(Equal(3))
Expect(period.DaysWithData).To(Equal(2))
Expect(period.Min).To(Equal(5.0))
Expect(period.Max).To(Equal(11.0))

err = period.Update(bucketFour)
Expect(err).ToNot(HaveOccurred())
Expect(period.Target.Records).To(Equal(2))
Expect(period.HoursWithData).To(Equal(4))
Expect(period.DaysWithData).To(Equal(2))
Expect(period.Min).To(Equal(5.0))
Expect(period.Max).To(Equal(11.0))

periodDays := 7
period.Finalize(periodDays)

Expect(period.AnyLow.Percent).To(Equal(0.0))
Expect(period.AnyHigh.Percent).To(Equal(0.5))
Expect(period.Target.Percent).To(Equal(0.5))
Expect(period.Low.Percent).To(Equal(0.0))
Expect(period.High.Percent).To(Equal(0.5))
Expect(period.VeryLow.Percent).To(Equal(0.0))
Expect(period.ExtremeHigh.Percent).To(Equal(0.0))

avgGlucose := (5.0 + 5.0 + 11.0 + 11.0) / 4.0
Expect(period.AverageDailyRecords).To(Equal(4.0 / float64(periodDays)))
Expect(period.AverageGlucose).To(Equal(avgGlucose))

expectedStdev := ((5-avgGlucose)*(5-avgGlucose) + (5-avgGlucose)*(5-avgGlucose) + (11-avgGlucose)*(11-avgGlucose) + (11-avgGlucose)*(11-avgGlucose)) / 4.0 / 3.0
expectedCV := expectedStdev / avgGlucose
Expect(period.StandardDeviation).To(Equal(expectedStdev))
Expect(period.CoefficientOfVariation).To(Equal(expectedCV))
})

It("Finalize a 1d period", func() {
period = GlucosePeriod{}
buckets := CreateGlucoseBuckets(bucketTime, 24, 12, true)
Expand Down