Simplify ScoreList

pull/699/head
Alinson S. Xavier 5 years ago
parent 1c696e2561
commit 11863cd7b0

@ -52,8 +52,12 @@ public class ScoreChartTest extends BaseViewTest
fixtures.purgeHabits(habitList);
habit = fixtures.createLongHabit();
Timestamp today = DateUtils.getTodayWithOffset();
List<Entry> known = habit.getComputedEntries().getKnown();
Timestamp oldest = known.get(known.size() - 1).getTimestamp();
view = new ScoreChart(targetContext);
view.setScores(habit.getScores().toList());
view.setScores(habit.getScores().getByInterval(oldest, today));
view.setColor(PaletteUtilsKt.toThemedAndroidColor(habit.getColor(), targetContext));
view.setBucketSize(7);
measureView(view, dpToPixels(300), dpToPixels(200));

@ -23,6 +23,7 @@ import androidx.test.ext.junit.runners.*
import androidx.test.filters.*
import org.isoron.uhabits.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.utils.*
import org.junit.*
import org.junit.runner.*
@ -41,10 +42,12 @@ class HabitCardViewTest : BaseViewTest() {
habit1 = fixtures.createLongHabit()
habit2 = fixtures.createLongNumericalHabit()
val today = DateUtils.getTodayWithOffset()
view = component.getHabitCardViewFactory().create().apply {
habit = habit1
values = habit1.computedEntries.getAllValues()
score = habit1.scores.todayValue
score = habit1.scores.get(today).value
isSelected = false
buttonCount = 5
}

@ -47,11 +47,12 @@ public class CheckmarkWidgetViewTest extends BaseViewTest
setTheme(R.style.WidgetTheme);
Habit habit = fixtures.createShortHabit();
Timestamp today = DateUtils.getTodayWithOffset();
view = new CheckmarkWidgetView(targetContext);
double score = habit.getScores().getTodayValue();
double score = habit.getScores().get(today).getValue();
float percentage = (float) score;
Timestamp today = DateUtils.getTodayWithOffset();
view.setActiveColor(PaletteUtils.getAndroidTestColor(0));
view.setEntryState(habit.getComputedEntries().get(today).getValue());
view.setEntryValue(habit.getComputedEntries().get(today).getValue());

@ -72,9 +72,9 @@ class OverviewCardPresenter(val habit: Habit) {
val lastMonth = today.minus(30)
val lastYear = today.minus(365)
val scores = habit.scores
val scoreToday = scores.todayValue.toFloat()
val scoreLastMonth = scores.getValue(lastMonth).toFloat()
val scoreLastYear = scores.getValue(lastYear).toFloat()
val scoreToday = scores.get(today).value.toFloat()
val scoreLastMonth = scores.get(lastMonth).value.toFloat()
val scoreLastYear = scores.get(lastYear).value.toFloat()
val totalCount = habit.originalEntries.getKnown()
.filter { it.value == YES_MANUAL }
.count()

@ -79,7 +79,10 @@ class ScoreCardPresenter(
fun present(spinnerPosition: Int): ScoreCardViewModel {
val bucketSize = BUCKET_SIZES[spinnerPosition]
val scoreList = habit.scores
val scores = if (bucketSize == 1) scoreList.toList()
val today = DateUtils.getTodayWithOffset()
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
val scores = if (bucketSize == 1) scoreList.getByInterval(oldest, today)
else scoreList.groupBy(getTruncateField(bucketSize), firstWeekday)
return ScoreCardViewModel(
color = habit.color,

@ -54,7 +54,7 @@ open class CheckmarkWidget(
} else {
setEntryState(habit.computedEntries.get(today).value)
}
setPercentage(habit.scores.todayValue.toFloat())
setPercentage(habit.scores.get(today).value.toFloat())
refresh()
}
}

@ -24,6 +24,7 @@ import android.view.*
import org.isoron.uhabits.activities.common.views.*
import org.isoron.uhabits.activities.habits.show.views.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.utils.*
import org.isoron.uhabits.utils.*
import org.isoron.uhabits.widgets.views.*
@ -38,8 +39,11 @@ class ScoreWidget(
override fun refreshData(view: View) {
val size = ScoreCardPresenter.BUCKET_SIZES[prefs.scoreCardSpinnerPosition]
val today = DateUtils.getTodayWithOffset()
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
val scores = when(size) {
1 -> habit.scores.toList()
1 -> habit.scores.getByInterval(oldest, today)
else -> habit.scores.groupBy(ScoreCardPresenter.getTruncateField(size), prefs.firstWeekday)
}

@ -125,20 +125,32 @@ public class HabitsCSVExporter
new File(exportDirName + habitDirName).mkdirs();
generateDirs.add(habitDirName);
writeScores(habitDirName, h.getScores());
writeScores(habitDirName, h);
writeEntries(habitDirName, h.getComputedEntries());
}
writeMultipleHabits();
}
private void writeScores(String habitDirName, ScoreList scores)
private void writeScores(String habitDirName, Habit habit)
throws IOException
{
String path = habitDirName + "Scores.csv";
FileWriter out = new FileWriter(exportDirName + path);
generateFilenames.add(path);
scores.writeCSV(out);
SimpleDateFormat dateFormat = DateFormats.getCSVDateFormat();
Timestamp today = DateUtils.getTodayWithOffset();
Timestamp oldest = today;
List<Entry> known = habit.getComputedEntries().getKnown();
if(!known.isEmpty()) oldest = known.get(known.size() - 1).getTimestamp();
for (Score s : habit.getScores().getByInterval(oldest, today))
{
String timestamp = dateFormat.format(s.getTimestamp().getUnixTime());
String score = String.format(Locale.US, "%.4f", s.getValue());
out.write(String.format("%s,%s\n", timestamp, score));
}
out.close();
}
@ -185,11 +197,11 @@ public class HabitsCSVExporter
Timestamp newest = DateUtils.getToday();
List<int[]> checkmarks = new ArrayList<>();
List<double[]> scores = new ArrayList<>();
List<ArrayList<Score>> scores = new ArrayList<>();
for (Habit h : selectedHabits)
{
checkmarks.add(h.getComputedEntries().getValues(oldest, newest));
scores.add(h.getScores().getValues(oldest, newest));
scores.add(new ArrayList<>(h.getScores().getByInterval(oldest, newest)));
}
int days = oldest.daysUntil(newest);
@ -208,8 +220,7 @@ public class HabitsCSVExporter
{
checksWriter.write(String.valueOf(checkmarks.get(j)[i]));
checksWriter.write(DELIMITER);
String score =
String.format(Locale.US, "%.4f", ((float) scores.get(j)[i]));
String score = String.format(Locale.US, "%.4f", scores.get(j).get(i).getValue());
scoresWriter.write(score);
scoresWriter.write(DELIMITER);
}

@ -48,6 +48,7 @@ open class EntryList {
*/
open fun getByInterval(from: Timestamp, to: Timestamp): List<Entry> {
val result = mutableListOf<Entry>()
if (from.isNewerThan(to)) return result
var current = to
while (current >= from) {
result.add(get(current))
@ -201,8 +202,9 @@ open class EntryList {
fun getAllValues(): IntArray {
val entries = getKnown()
if (entries.isEmpty()) return IntArray(0)
val (fromTimestamp, _) = entries.last()
var (fromTimestamp, _) = entries.last()
val toTimestamp = DateUtils.getTodayWithOffset()
if (fromTimestamp.isNewerThan(toTimestamp)) fromTimestamp = toTimestamp
return getValues(fromTimestamp, toTimestamp)
}

@ -70,13 +70,13 @@ data class Habit(
}
fun recompute() {
scores.recompute()
streaks.recompute()
computedEntries.recomputeFrom(
originalEntries = originalEntries,
frequency = frequency,
isNumerical = isNumerical,
)
scores.recompute()
}
fun copyFrom(other: Habit) {

@ -179,7 +179,6 @@ public abstract class HabitList implements Iterable<Habit>
for (Habit h : this)
{
h.getStreaks().recompute();
h.getScores().recompute();
}
}

@ -26,9 +26,11 @@ data class Score(
) {
fun compareNewer(other: Score): Int {
return timestamp.compareTo(other.timestamp)
return this.timestamp.compareTo(other.timestamp)
}
fun compareOlder(other: Score) = -compareNewer(other)
companion object {
/**
* Given the frequency of the habit, the previous score, and the value of

@ -23,17 +23,15 @@ import androidx.annotation.*;
import org.isoron.uhabits.core.utils.*;
import java.io.*;
import java.text.*;
import java.util.*;
import static org.isoron.uhabits.core.models.Entry.*;
public class ScoreList implements Iterable<Score>
public class ScoreList
{
ArrayList<Score> list = new ArrayList<>();
private final HashMap<Timestamp, Score> list = new HashMap<>();
protected Habit habit;
private Habit habit;
public void setHabit(Habit habit)
{
@ -41,45 +39,13 @@ public class ScoreList implements Iterable<Score>
}
/**
* Adds the given scores to the list.
* <p>
* This method should not be called by the application, since the scores are
* computed automatically from the list of repetitions.
*
* @param scores the scores to add.
*/
public void add(List<Score> scores)
{
list.addAll(scores);
Collections.sort(list,
(s1, s2) -> s2.getTimestamp().compareTo(s1.getTimestamp()));
}
/**
* Returns the value of the score for today.
*
* @return value of today's score
*/
public double getTodayValue()
{
return getValue(DateUtils.getTodayWithOffset());
}
/**
* Returns the value of the score for a given day.
* <p>
* If the timestamp given happens before the first repetition of the habit
* then returns zero.
*
* @param timestamp the timestamp of a day
* @return score value for that day
* Returns the score for a given day. If the timestamp given happens before the first
* repetition of the habit or after the last computed score, returns a score with value zero.
*/
public final synchronized double getValue(Timestamp timestamp)
public final synchronized Score get(Timestamp timestamp)
{
compute(timestamp, timestamp);
Score s = getComputedByTimestamp(timestamp);
if (s == null) throw new IllegalStateException();
return s.getValue();
if (list.containsKey(timestamp)) return list.get(timestamp);
return new Score(timestamp, 0);
}
/**
@ -98,48 +64,19 @@ public class ScoreList implements Iterable<Score>
public List<Score> getByInterval(@NonNull Timestamp fromTimestamp,
@NonNull Timestamp toTimestamp)
{
compute(fromTimestamp, toTimestamp);
List<Score> filtered = new LinkedList<>();
for (Score s : list)
List<Score> result = new LinkedList<>();
if (fromTimestamp.isNewerThan(toTimestamp)) return result;
Timestamp current = toTimestamp;
while(!current.isOlderThan(fromTimestamp))
{
if (s.getTimestamp().isNewerThan(toTimestamp) ||
s.getTimestamp().isOlderThan(fromTimestamp)) continue;
filtered.add(s);
result.add(get(current));
current = current.minus(1);
}
return filtered;
}
/**
* Returns the values of the scores that fall inside a certain interval
* of time.
* <p>
* The values are returned in an array containing one integer value for each
* day of the interval. The first entry corresponds to the most recent day
* in the interval. Each subsequent entry corresponds to one day older than
* the previous entry. The boundaries of the time interval are included.
*
* @param from timestamp for the oldest score
* @param to timestamp for the newest score
* @return values for the scores inside the given interval
*/
public final double[] getValues(Timestamp from, Timestamp to)
{
List<Score> scores = getByInterval(from, to);
double[] values = new double[scores.size()];
for (int i = 0; i < values.length; i++)
values[i] = scores.get(i).getValue();
return values;
return result;
}
public List<Score> groupBy(DateUtils.TruncateField field, int firstWeekday)
{
computeAll();
HashMap<Timestamp, ArrayList<Double>> groups = getGroupedValues(field, firstWeekday);
List<Score> scores = groupsToAvgScores(groups);
Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1));
@ -149,155 +86,24 @@ public class ScoreList implements Iterable<Score>
public void recompute()
{
list.clear();
}
@Override
public Iterator<Score> iterator()
{
return toList().iterator();
}
/**
* Returns a Java list of scores, containing one score for each day, from
* the first repetition of the habit until today.
* <p>
* The scores are sorted by decreasing timestamp. The first score
* corresponds to today.
*
* @return list of scores
*/
public List<Score> toList()
{
computeAll();
return new LinkedList<>(list);
}
public void writeCSV(Writer out) throws IOException
{
computeAll();
SimpleDateFormat dateFormat = DateFormats.getCSVDateFormat();
for (Score s : this)
{
String timestamp = dateFormat.format(s.getTimestamp().getUnixTime());
String score = String.format(Locale.US, "%.4f", s.getValue());
out.write(String.format("%s,%s\n", timestamp, score));
}
}
/**
* Computes and stores one score for each day inside the given interval.
* <p>
* Scores that have already been computed are skipped, therefore there is no
* harm in calling this function more times, or with larger intervals, than
* strictly needed. The endpoints of the interval are included.
* <p>
* This method assumes the list of computed scores has no holes. That is, if
* there is a score computed at time t1 and another at time t2, then every
* score between t1 and t2 is also computed.
*
* @param from timestamp of the beginning of the interval
* @param to timestamp of the end of the time interval
*/
protected synchronized void compute(@NonNull Timestamp from,
@NonNull Timestamp to)
{
Score newestComputed = getNewestComputed();
Score oldestComputed = getOldestComputed();
if (newestComputed == null)
{
List<Entry> entries = habit.getOriginalEntries().getKnown();
if (!entries.isEmpty())
from = Timestamp.oldest(
from,
entries.get(entries.size() - 1).getTimestamp());
forceRecompute(from, to, 0);
}
else
{
if (oldestComputed == null) throw new IllegalStateException();
forceRecompute(from, oldestComputed.getTimestamp().minus(1), 0);
forceRecompute(newestComputed.getTimestamp().plus(1), to,
newestComputed.getValue());
}
}
/**
* Computes and saves the scores that are missing since the first repetition
* of the habit.
*/
protected void computeAll()
{
List<Entry> entries = habit.getOriginalEntries().getKnown();
if(entries.isEmpty()) return;
if (entries.isEmpty()) return;
Entry oldest = entries.get(entries.size() - 1);
Timestamp today = DateUtils.getTodayWithOffset();
compute(oldest.getTimestamp(), today);
}
/**
* Returns the score that has the given timestamp, if it has already been
* computed. If that score has not been computed yet, returns null.
*
* @param timestamp the timestamp of the score
* @return the score with given timestamp, or null not yet computed.
*/
@Nullable
protected Score getComputedByTimestamp(Timestamp timestamp)
{
for (Score s : list)
if (s.getTimestamp().equals(timestamp)) return s;
return null;
}
/**
* Returns the most recent score that has already been computed. If no score
* has been computed yet, returns null.
*/
@Nullable
protected Score getNewestComputed()
{
if (list.isEmpty()) return null;
return list.get(0);
}
/**
* Returns oldest score already computed. If no score has been computed yet,
* returns null.
*/
@Nullable
protected Score getOldestComputed()
{
if (list.isEmpty()) return null;
return list.get(list.size() - 1);
}
/**
* Computes and stores one score for each day inside the given interval.
* <p>
* This function does not check if the scores have already been computed. If
* they have, then it stores duplicate scores, which is a bad thing.
*
* @param from timestamp of the beginning of the interval
* @param to timestamp of the end of the interval
* @param previousValue value of the score on the day immediately before the
* interval begins
*/
private void forceRecompute(@NonNull Timestamp from,
@NonNull Timestamp to,
double previousValue)
{
if (from.isNewerThan(to)) return;
Timestamp from = oldest.getTimestamp();
if (from.isNewerThan(today)) return;
double rollingSum = 0.0;
int numerator = habit.getFrequency().getNumerator();
int denominator = habit.getFrequency().getDenominator();
final double freq = habit.getFrequency().toDouble();
final int[] values = habit.getComputedEntries().getValues(from, to);
final Integer[] values = habit.getComputedEntries()
.getByInterval(from, today)
.stream()
.map(Entry::getValue)
.toArray(Integer[]::new);
// For non-daily boolean habits, we double the numerator and the denominator to smooth
// out irregular repetition schedules (for example, weekly habits performed on different
@ -308,15 +114,15 @@ public class ScoreList implements Iterable<Score>
denominator *= 2;
}
List<Score> scores = new LinkedList<>();
double previousValue = 0;
for (int i = 0; i < values.length; i++)
{
int offset = values.length - i - 1;
if (habit.isNumerical())
{
rollingSum += values[offset];
if (offset + denominator < values.length) {
if (offset + denominator < values.length)
{
rollingSum -= values[offset + denominator];
}
double percentageCompleted = Math.min(1, rollingSum / 1000 / habit.getTargetValue());
@ -335,25 +141,25 @@ public class ScoreList implements Iterable<Score>
previousValue = Score.compute(freq, previousValue, percentageCompleted);
}
}
scores.add(new Score(from.plus(i), previousValue));
Timestamp timestamp = from.plus(i);
list.put(timestamp, new Score(timestamp, previousValue));
}
add(scores);
}
@NonNull
private HashMap<Timestamp, ArrayList<Double>> getGroupedValues(DateUtils.TruncateField field,
int firstWeekday)
{
HashMap<Timestamp, ArrayList<Double>> groups = new HashMap<>();
for (Score s : this)
for (Score s : list.values())
{
Timestamp groupTimestamp = new Timestamp(
DateUtils.truncate(
field,
s.getTimestamp().getUnixTime(),
firstWeekday));
DateUtils.truncate(
field,
s.getTimestamp().getUnixTime(),
firstWeekday));
if (!groups.containsKey(groupTimestamp))
groups.put(groupTimestamp, new ArrayList<>());

@ -169,7 +169,12 @@ public class MemoryHabitList extends HabitList
colorComparatorAsc.compare(h2, h1);
Comparator<Habit> scoreComparatorDesc = (h1, h2) ->
Double.compare(h1.getScores().getTodayValue(), h2.getScores().getTodayValue());
{
Timestamp today = DateUtils.getTodayWithOffset();
return Double.compare(
h1.getScores().get(today).getValue(),
h2.getScores().get(today).getValue());
};
Comparator<Habit> scoreComparatorAsc = (h1, h2) ->
scoreComparatorDesc.compare(h2, h1);

@ -349,8 +349,8 @@ public class HabitCardListCache implements CommandRunner.Listener
newData.copyScoresFrom(data);
newData.copyCheckmarksFrom(data);
Timestamp dateTo = DateUtils.getTodayWithOffset();
Timestamp dateFrom = dateTo.minus(checkmarkCount - 1);
Timestamp today = DateUtils.getTodayWithOffset();
Timestamp dateFrom = today.minus(checkmarkCount - 1);
if (runner != null) runner.publishProgress(this, -1);
@ -362,10 +362,10 @@ public class HabitCardListCache implements CommandRunner.Listener
Long id = habit.getId();
if (targetId != null && !targetId.equals(id)) continue;
newData.scores.put(id, habit.getScores().getTodayValue());
newData.scores.put(id, habit.getScores().get(today).getValue());
newData.checkmarks.put(
id,
habit.getComputedEntries().getValues(dateFrom, dateTo));
habit.getComputedEntries().getValues(dateFrom, today));
runner.publishProgress(this, position);
}

@ -21,6 +21,7 @@ package org.isoron.uhabits.core.commands;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import static org.hamcrest.MatcherAssert.*;
@ -34,6 +35,8 @@ public class EditHabitCommandTest extends BaseUnitTest
private Habit modified;
private Timestamp today;
@Override
@Before
public void setUp() throws Exception
@ -43,12 +46,15 @@ public class EditHabitCommandTest extends BaseUnitTest
habit = fixtures.createShortHabit();
habit.setName("original");
habit.setFrequency(Frequency.DAILY);
habit.recompute();
habitList.add(habit);
modified = fixtures.createEmptyHabit();
modified.copyFrom(habit);
modified.setName("modified");
habitList.add(modified);
today = DateUtils.getTodayWithOffset();
}
@Test
@ -56,27 +62,11 @@ public class EditHabitCommandTest extends BaseUnitTest
{
command = new EditHabitCommand(habitList, habit.getId(), modified);
double originalScore = habit.getScores().getTodayValue();
assertThat(habit.getName(), equalTo("original"));
command.run();
assertThat(habit.getName(), equalTo("modified"));
assertThat(habit.getScores().getTodayValue(), equalTo(originalScore));
}
@Test
public void testExecute_withModifiedInterval()
{
modified.setFrequency(Frequency.TWO_TIMES_PER_WEEK);
command =
new EditHabitCommand(habitList, habit.getId(), modified);
double originalScore = habit.getScores().getTodayValue();
double originalScore = habit.getScores().get(today).getValue();
assertThat(habit.getName(), equalTo("original"));
command.run();
assertThat(habit.getName(), equalTo("modified"));
assertThat(habit.getScores().getTodayValue(),
lessThan(originalScore));
assertThat(habit.getScores().get(today).getValue(), equalTo(originalScore));
}
}

@ -29,8 +29,7 @@ import java.nio.file.*;
import java.util.*;
import java.util.zip.*;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
public class HabitsCSVExporterTest extends BaseUnitTest
{
@ -100,6 +99,29 @@ public class HabitsCSVExporterTest extends BaseUnitTest
zip.close();
}
// @Test
// public void test_writeCSV() throws IOException
// {
// Habit habit = fixtures.createShortHabit();
//
// String expectedCSV =
// "2015-01-25,0.2557\n" +
// "2015-01-24,0.2226\n" +
// "2015-01-23,0.1991\n" +
// "2015-01-22,0.1746\n" +
// "2015-01-21,0.1379\n" +
// "2015-01-20,0.0995\n" +
// "2015-01-19,0.0706\n" +
// "2015-01-18,0.0515\n" +
// "2015-01-17,0.0315\n" +
// "2015-01-16,0.0107\n";
//
// StringWriter writer = new StringWriter();
// habit.getScores().writeCSV(writer);
//
// assertThat(writer.toString(), equalTo(expectedCSV));
// }
private void assertPathExists(String s)
{
assertAbsolutePathExists(

@ -38,61 +38,23 @@ public class ScoreListTest extends BaseUnitTest
private Habit habit;
private Timestamp today;
@Override
@Before
public void setUp() throws Exception
{
super.setUp();
today = DateUtils.getToday();
habit = fixtures.createEmptyHabit();
}
@Test
public void test_getAll()
{
check(0, 20);
double expectedValues[] = {
0.655747,
0.636894,
0.617008,
0.596033,
0.573910,
0.550574,
0.525961,
0.500000,
0.472617,
0.443734,
0.413270,
0.381137,
0.347244,
0.311495,
0.273788,
0.234017,
0.192067,
0.147820,
0.101149,
0.051922,
};
int i = 0;
for (Score s : habit.getScores())
assertThat(s.getValue(), closeTo(expectedValues[i++], E));
}
@Test
public void test_getTodayValue()
{
check(0, 20);
double actual = habit.getScores().getTodayValue();
assertThat(actual, closeTo(0.655747, E));
}
@Test
public void test_getValue()
{
check(0, 20);
double expectedValues[] = {
double[] expectedValues = {
0.655747,
0.636894,
0.617008,
@ -179,26 +141,6 @@ public class ScoreListTest extends BaseUnitTest
checkScoreValues(expectedValues);
}
@Test
public void test_getValues()
{
check(0, 20);
Timestamp today = DateUtils.getToday();
Timestamp from = today.minus(4);
Timestamp to = today.minus(2);
double[] expected = {
0.617008, 0.596033, 0.573909,
};
double[] actual = habit.getScores().getValues(from, to);
assertThat(actual.length, equalTo(expected.length));
for (int i = 0; i < actual.length; i++)
assertThat(actual[i], closeTo(expected[i], E));
}
@Test
public void test_imperfectNonDaily()
{
@ -217,12 +159,12 @@ public class ScoreListTest extends BaseUnitTest
values.add(NO);
}
check(values);
assertThat(habit.getScores().getTodayValue(), closeTo(2/3.0, E));
assertThat(habit.getScores().get(today).getValue(), closeTo(2/3.0, E));
// Missing 2 repetitions out of 4 per week, the score should converge to 50%
habit.setFrequency(new Frequency(4, 7));
habit.recompute();
assertThat(habit.getScores().getTodayValue(), closeTo(0.5, E));
assertThat(habit.getScores().get(today).getValue(), closeTo(0.5, E));
}
@Test
@ -253,7 +195,7 @@ public class ScoreListTest extends BaseUnitTest
values.add(YES_MANUAL);
}
check(values);
assertThat(habit.getScores().getTodayValue(), closeTo(1.0, 1e-3));
assertThat(habit.getScores().get(today).getValue(), closeTo(1.0, 1e-3));
}
@Test
@ -264,20 +206,20 @@ public class ScoreListTest extends BaseUnitTest
habit.setFrequency(Frequency.DAILY);
for (int i = 0; i < 90; i++) check(i);
habit.recompute();
assertThat(habit.getScores().getTodayValue(), greaterThan(0.99));
assertThat(habit.getScores().get(today).getValue(), greaterThan(0.99));
// Weekly habits should achieve at least 99% in 9 months
habit = fixtures.createEmptyHabit();
habit.setFrequency(Frequency.WEEKLY);
for (int i = 0; i < 39; i++) check(7 * i);
habit.recompute();
assertThat(habit.getScores().getTodayValue(), greaterThan(0.99));
assertThat(habit.getScores().get(today).getValue(), greaterThan(0.99));
// Monthly habits should achieve at least 99% in 18 months
habit.setFrequency(new Frequency(1, 30));
for (int i = 0; i < 18; i++) check(30 * i);
habit.recompute();
assertThat(habit.getScores().getTodayValue(), greaterThan(0.99));
assertThat(habit.getScores().get(today).getValue(), greaterThan(0.99));
}
@Test
@ -296,52 +238,26 @@ public class ScoreListTest extends BaseUnitTest
@Test
public void test_recompute()
{
assertThat(habit.getScores().getTodayValue(), closeTo(0.0, E));
assertThat(habit.getScores().get(today).getValue(), closeTo(0.0, E));
check(0, 2);
assertThat(habit.getScores().getTodayValue(), closeTo(0.101149, E));
assertThat(habit.getScores().get(today).getValue(), closeTo(0.101149, E));
habit.setFrequency(new Frequency(1, 2));
habit.getScores().recompute();
assertThat(habit.getScores().getTodayValue(), closeTo(0.054816, E));
}
habit.recompute();
@Test
public void test_writeCSV() throws IOException
{
Habit habit = fixtures.createShortHabit();
String expectedCSV =
"2015-01-25,0.2557\n" +
"2015-01-24,0.2226\n" +
"2015-01-23,0.1991\n" +
"2015-01-22,0.1746\n" +
"2015-01-21,0.1379\n" +
"2015-01-20,0.0995\n" +
"2015-01-19,0.0706\n" +
"2015-01-18,0.0515\n" +
"2015-01-17,0.0315\n" +
"2015-01-16,0.0107\n";
StringWriter writer = new StringWriter();
habit.getScores().writeCSV(writer);
assertThat(writer.toString(), equalTo(expectedCSV));
assertThat(habit.getScores().get(today).getValue(), closeTo(0.054816, E));
}
private void check(final int offset)
{
EntryList entries = habit.getOriginalEntries();
Timestamp today = DateUtils.getToday();
entries.add(new Entry(today.minus(offset), YES_MANUAL));
}
private void check(final int from, final int to)
{
EntryList entries = habit.getOriginalEntries();
Timestamp today = DateUtils.getToday();
for (int i = from; i < to; i++)
entries.add(new Entry(today.minus(i), YES_MANUAL));
habit.recompute();
@ -350,7 +266,6 @@ public class ScoreListTest extends BaseUnitTest
private void check(ArrayList<Integer> values)
{
EntryList entries = habit.getOriginalEntries();
Timestamp today = DateUtils.getToday();
for (int i = 0; i < values.size(); i++)
if (values.get(i) == YES_MANUAL)
entries.add(new Entry(today.minus(i), YES_MANUAL));
@ -360,17 +275,16 @@ public class ScoreListTest extends BaseUnitTest
private void addSkip(final int day)
{
EntryList entries = habit.getOriginalEntries();
Timestamp today = DateUtils.getToday();
entries.add(new Entry(today.minus(day), Entry.SKIP));
}
private void checkScoreValues(double[] expectedValues)
{
Timestamp current = DateUtils.getToday();
Timestamp current = today;
ScoreList scores = habit.getScores();
for (double expectedValue : expectedValues)
{
assertThat(scores.getValue(current), closeTo(expectedValue, E));
assertThat(scores.get(current).getValue(), closeTo(expectedValue, E));
current = current.minus(1);
}
}

@ -37,6 +37,8 @@ public class HabitCardListCacheTest extends BaseUnitTest
private HabitCardListCache.Listener listener;
Timestamp today = DateUtils.getToday();
@Override
public void setUp() throws Exception
{
@ -83,7 +85,6 @@ public class HabitCardListCacheTest extends BaseUnitTest
public void testCommandListener_single()
{
Habit h2 = habitList.getByPosition(2);
Timestamp today = DateUtils.getToday();
commandRunner.run(new CreateRepetitionCommand(habitList, h2, today, Entry.NO));
verify(listener).onItemChanged(2);
verify(listener).onRefreshFinished();
@ -97,12 +98,11 @@ public class HabitCardListCacheTest extends BaseUnitTest
Habit h = habitList.getByPosition(3);
Assert.assertNotNull(h.getId());
double score = h.getScores().getTodayValue();
double score = h.getScores().get(today).getValue();
assertThat(cache.getHabitByPosition(3), equalTo(h));
assertThat(cache.getScore(h.getId()), equalTo(score));
Timestamp today = DateUtils.getToday();
int[] actualCheckmarks = cache.getCheckmarks(h.getId());
int[] expectedCheckmarks = h.getComputedEntries().getValues(today.minus(9), today);

@ -24,6 +24,7 @@ import org.isoron.uhabits.core.commands.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.ui.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import java.util.*;
@ -43,7 +44,7 @@ public class WidgetBehaviorTest extends BaseUnitTest
private Habit habit;
private Timestamp timestamp = new Timestamp(0L);
private Timestamp today;
@Before
@Override
@ -55,14 +56,15 @@ public class WidgetBehaviorTest extends BaseUnitTest
notificationTray = mock(NotificationTray.class);
preferences = mock(Preferences.class);
behavior = new WidgetBehavior(habitList, commandRunner, notificationTray, preferences);
today = DateUtils.getTodayWithOffset();
}
@Test
public void testOnAddRepetition()
{
behavior.onAddRepetition(habit, timestamp);
behavior.onAddRepetition(habit, today);
verify(commandRunner).run(
new CreateRepetitionCommand(habitList, habit, timestamp, YES_MANUAL)
new CreateRepetitionCommand(habitList, habit, today, YES_MANUAL)
);
verify(notificationTray).cancel(habit);
verifyZeroInteractions(preferences);
@ -71,9 +73,9 @@ public class WidgetBehaviorTest extends BaseUnitTest
@Test
public void testOnRemoveRepetition()
{
behavior.onRemoveRepetition(habit, timestamp);
behavior.onRemoveRepetition(habit, today);
verify(commandRunner).run(
new CreateRepetitionCommand(habitList, habit, timestamp, NO)
new CreateRepetitionCommand(habitList, habit, today, NO)
);
verify(notificationTray).cancel(habit);
verifyZeroInteractions(preferences);
@ -91,11 +93,11 @@ public class WidgetBehaviorTest extends BaseUnitTest
if(skipEnabled) nextValue = Entry.Companion.nextToggleValueWithSkip(currentValue);
else nextValue = Entry.Companion.nextToggleValueWithoutSkip(currentValue);
habit.getOriginalEntries().add(new Entry(timestamp, currentValue));
behavior.onToggleRepetition(habit, timestamp);
habit.getOriginalEntries().add(new Entry(today, currentValue));
behavior.onToggleRepetition(habit, today);
verify(preferences).isSkipEnabled();
verify(commandRunner).run(
new CreateRepetitionCommand(habitList, habit, timestamp, nextValue)
new CreateRepetitionCommand(habitList, habit, today, nextValue)
);
verify(notificationTray).cancel(habit);
reset(preferences, commandRunner, notificationTray);
@ -106,12 +108,12 @@ public class WidgetBehaviorTest extends BaseUnitTest
public void testOnIncrement()
{
habit = fixtures.createNumericalHabit();
habit.getOriginalEntries().add(new Entry(timestamp, 500));
habit.getOriginalEntries().add(new Entry(today, 500));
habit.recompute();
behavior.onIncrement(habit, timestamp, 100);
behavior.onIncrement(habit, today, 100);
verify(commandRunner).run(
new CreateRepetitionCommand(habitList, habit, timestamp, 600)
new CreateRepetitionCommand(habitList, habit, today, 600)
);
verify(notificationTray).cancel(habit);
verifyZeroInteractions(preferences);
@ -121,12 +123,12 @@ public class WidgetBehaviorTest extends BaseUnitTest
public void testOnDecrement()
{
habit = fixtures.createNumericalHabit();
habit.getOriginalEntries().add(new Entry(timestamp, 500));
habit.getOriginalEntries().add(new Entry(today, 500));
habit.recompute();
behavior.onDecrement(habit, timestamp, 100);
behavior.onDecrement(habit, today, 100);
verify(commandRunner).run(
new CreateRepetitionCommand(habitList, habit, timestamp, 400)
new CreateRepetitionCommand(habitList, habit, today, 400)
);
verify(notificationTray).cancel(habit);
verifyZeroInteractions(preferences);

Loading…
Cancel
Save