From 8b847ae9fa09d1250d63218f35c0d347cd4991bd Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Tue, 15 Sep 2020 21:02:04 -0500 Subject: [PATCH] ScoreList: Use rolling sum method also for boolean habits See #641 --- .../org/isoron/uhabits/core/models/Habit.java | 1 + .../isoron/uhabits/core/models/ScoreList.java | 13 ++++- .../uhabits/core/models/ScoreListTest.java | 54 ++++++++++++++++--- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java index 5b2700514..6d2a555cd 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java @@ -162,6 +162,7 @@ public class Habit public synchronized void setFrequency(@NonNull Frequency frequency) { data.frequency = frequency; + invalidateNewerThan(Timestamp.ZERO); } @Nullable diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java index 26129e689..014b89812 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java @@ -27,6 +27,8 @@ import java.io.*; import java.text.*; import java.util.*; +import static org.isoron.uhabits.core.models.Checkmark.*; + public abstract class ScoreList implements Iterable { protected final Habit habit; @@ -268,6 +270,7 @@ public abstract class ScoreList implements Iterable if (from.isNewerThan(to)) return; double rollingSum = 0.0; + int numerator = habit.getFrequency().getNumerator(); int denominator = habit.getFrequency().getDenominator(); final double freq = habit.getFrequency().toDouble(); final int[] checkmarkValues = habit.getCheckmarks().getValues(from, to); @@ -288,8 +291,14 @@ public abstract class ScoreList implements Iterable } else if (checkmarkValues[offset] != Checkmark.SKIP) { - double value = Math.min(1, checkmarkValues[offset]); - previousValue = Score.compute(freq, previousValue, value); + if (checkmarkValues[offset] == YES_MANUAL) + rollingSum += 1.0; + if (offset + denominator < checkmarkValues.length) + if (checkmarkValues[offset + denominator] == YES_MANUAL) + rollingSum -= 1.0; + + double percentageCompleted = Math.min(1, rollingSum / numerator); + previousValue = Score.compute(freq, previousValue, percentageCompleted); } scores.add(new Score(from.plus(i), previousValue)); } diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java index d27f9727c..64b05266e 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java @@ -29,6 +29,7 @@ import java.util.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.core.IsEqual.*; import static org.hamcrest.number.IsCloseTo.*; +import static org.isoron.uhabits.core.models.Checkmark.*; public class ScoreListTest extends BaseUnitTest { @@ -188,6 +189,28 @@ public class ScoreListTest extends BaseUnitTest assertThat(actual[i], closeTo(expected[i], E)); } + @Test + public void test_imperfectNonDaily() + { + habit.setFrequency(new Frequency(3, 7)); + ArrayList values = new ArrayList<>(); + for (int k = 0; k < 100; k++) + { + values.add(YES_MANUAL); + values.add(YES_MANUAL); + values.add(NO); + values.add(NO); + values.add(NO); + values.add(NO); + values.add(NO); + } + toggle(values); + assertThat(habit.getScores().getTodayValue(), closeTo(2/3.0, E)); + + habit.setFrequency(new Frequency(4, 7)); + assertThat(habit.getScores().getTodayValue(), closeTo(0.5, E)); + } + @Test public void test_groupBy() { @@ -196,9 +219,9 @@ public class ScoreListTest extends BaseUnitTest habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SATURDAY); assertThat(list.size(), equalTo(5)); - assertThat(list.get(0).getValue(), closeTo(0.687724, E)); - assertThat(list.get(1).getValue(), closeTo(0.636747, E)); - assertThat(list.get(2).getValue(), closeTo(0.533860, E)); + assertThat(list.get(0).getValue(), closeTo(0.601508, E)); + assertThat(list.get(1).getValue(), closeTo(0.580580, E)); + assertThat(list.get(2).getValue(), closeTo(0.474609, E)); } @Test @@ -220,11 +243,17 @@ public class ScoreListTest extends BaseUnitTest { Habit habit = fixtures.createShortHabit(); - String expectedCSV = "2015-01-25,0.2654\n2015-01-24,0.2389\n" + - "2015-01-23,0.2475\n2015-01-22,0.2203\n" + - "2015-01-21,0.1921\n2015-01-20,0.1628\n" + - "2015-01-19,0.1325\n2015-01-18,0.1011\n" + - "2015-01-17,0.0686\n2015-01-16,0.0349\n"; + String expectedCSV = + "2015-01-25,0.2234\n" + + "2015-01-24,0.2134\n" + + "2015-01-23,0.2031\n" + + "2015-01-22,0.1742\n" + + "2015-01-21,0.1443\n" + + "2015-01-20,0.1134\n" + + "2015-01-19,0.0994\n" + + "2015-01-18,0.0849\n" + + "2015-01-17,0.0518\n" + + "2015-01-16,0.0175\n"; StringWriter writer = new StringWriter(); habit.getScores().writeCSV(writer); @@ -241,6 +270,15 @@ public class ScoreListTest extends BaseUnitTest reps.toggle(today.minus(i)); } + private void toggle(ArrayList values) + { + RepetitionList reps = habit.getRepetitions(); + Timestamp today = DateUtils.getToday(); + for (int i = 0; i < values.size(); i++) + if (values.get(i) == YES_MANUAL) + reps.toggle(today.minus(i)); + } + private void addSkip(final int day) { RepetitionList reps = habit.getRepetitions();