mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Fix bug on compute methods that prevented them from recomputing old values
This commit is contained in:
@@ -49,7 +49,7 @@ public class ScoreChartTest extends BaseViewTest
|
|||||||
habit = fixtures.createLongHabit();
|
habit = fixtures.createLongHabit();
|
||||||
|
|
||||||
view = new ScoreChart(targetContext);
|
view = new ScoreChart(targetContext);
|
||||||
view.setScores(habit.getScores().getAll());
|
view.setScores(habit.getScores().toList());
|
||||||
view.setColor(ColorUtils.getColor(targetContext, habit.getColor()));
|
view.setColor(ColorUtils.getColor(targetContext, habit.getColor()));
|
||||||
view.setBucketSize(7);
|
view.setBucketSize(7);
|
||||||
measureView(view, dpToPixels(300), dpToPixels(200));
|
measureView(view, dpToPixels(300), dpToPixels(200));
|
||||||
|
|||||||
@@ -92,6 +92,16 @@ public class SQLiteCheckmarkListTest extends BaseAndroidTest
|
|||||||
assertThat(list.get(7).getTimestamp(), equalTo(today - 10 * day));
|
assertThat(list.get(7).getTimestamp(), equalTo(today - 10 * day));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetByInterval_withLongInterval()
|
||||||
|
{
|
||||||
|
long from = today - 200 * day;
|
||||||
|
long to = today;
|
||||||
|
|
||||||
|
List<Checkmark> list = checkmarks.getByInterval(from, to);
|
||||||
|
assertThat(list.size(), equalTo(201));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvalidateNewerThan()
|
public void testInvalidateNewerThan()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ import org.junit.runner.*;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertNotNull;
|
|
||||||
import static junit.framework.Assert.assertNull;
|
|
||||||
import static org.hamcrest.MatcherAssert.*;
|
import static org.hamcrest.MatcherAssert.*;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
@@ -66,7 +64,7 @@ public class SQLiteScoreListTest extends BaseAndroidTest
|
|||||||
@Test
|
@Test
|
||||||
public void testGetAll()
|
public void testGetAll()
|
||||||
{
|
{
|
||||||
List<Score> list = scores.getAll();
|
List<Score> list = scores.toList();
|
||||||
assertThat(list.size(), equalTo(121));
|
assertThat(list.size(), equalTo(121));
|
||||||
assertThat(list.get(0).getTimestamp(), equalTo(today));
|
assertThat(list.get(0).getTimestamp(), equalTo(today));
|
||||||
assertThat(list.get(10).getTimestamp(), equalTo(today - 10 * day));
|
assertThat(list.get(10).getTimestamp(), equalTo(today - 10 * day));
|
||||||
@@ -103,17 +101,6 @@ public class SQLiteScoreListTest extends BaseAndroidTest
|
|||||||
assertThat(records.get(0).timestamp, equalTo(today));
|
assertThat(records.get(0).timestamp, equalTo(today));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetByTimestamp()
|
|
||||||
{
|
|
||||||
Score s = scores.getByTimestamp(today);
|
|
||||||
assertNotNull(s);
|
|
||||||
assertThat(s.getTimestamp(), equalTo(today));
|
|
||||||
|
|
||||||
s = scores.getByTimestamp(today - 200 * day);
|
|
||||||
assertNull(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ScoreRecord> getAllRecords()
|
private List<ScoreRecord> getAllRecords()
|
||||||
{
|
{
|
||||||
return new Select()
|
return new Select()
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ public class ScoreCard extends HabitCard
|
|||||||
List<Score> scores;
|
List<Score> scores;
|
||||||
ScoreList scoreList = getHabit().getScores();
|
ScoreList scoreList = getHabit().getScores();
|
||||||
|
|
||||||
if (bucketSize == 1) scores = scoreList.getAll();
|
if (bucketSize == 1) scores = scoreList.toList();
|
||||||
else scores = scoreList.groupBy(getTruncateField(bucketSize));
|
else scores = scoreList.groupBy(getTruncateField(bucketSize));
|
||||||
|
|
||||||
chart.setScores(scores);
|
chart.setScores(scores);
|
||||||
|
|||||||
@@ -176,19 +176,54 @@ public abstract class CheckmarkList
|
|||||||
* specified interval of time. Days that already have a corresponding
|
* specified interval of time. Days that already have a corresponding
|
||||||
* checkmark are skipped.
|
* checkmark are skipped.
|
||||||
*
|
*
|
||||||
|
* This method assumes the list of computed checkmarks has no holes. That
|
||||||
|
* is, if there is a checkmark computed at time t1 and another at time t2,
|
||||||
|
* then every checkmark between t1 and t2 is also computed.
|
||||||
|
*
|
||||||
* @param from timestamp for the beginning of the interval
|
* @param from timestamp for the beginning of the interval
|
||||||
* @param to timestamp for the end of the interval
|
* @param to timestamp for the end of the interval
|
||||||
*/
|
*/
|
||||||
protected final synchronized void compute(long from, final long to)
|
protected final synchronized void compute(long from, long to)
|
||||||
{
|
{
|
||||||
final long day = DateUtils.millisecondsInOneDay;
|
final long day = DateUtils.millisecondsInOneDay;
|
||||||
|
|
||||||
Checkmark newestCheckmark = getNewestComputed();
|
Checkmark newest = getNewestComputed();
|
||||||
if (newestCheckmark != null)
|
Checkmark oldest = getOldestComputed();
|
||||||
from = newestCheckmark.getTimestamp() + day;
|
|
||||||
|
|
||||||
|
if (newest == null)
|
||||||
|
{
|
||||||
|
forceRecompute(from, to);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
forceRecompute(from, oldest.getTimestamp() - day);
|
||||||
|
forceRecompute(newest.getTimestamp() + day, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns oldest checkmark that has already been computed.
|
||||||
|
*
|
||||||
|
* @return oldest checkmark already computed
|
||||||
|
*/
|
||||||
|
protected abstract Checkmark getOldestComputed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and stores one checkmark for each day that falls inside the
|
||||||
|
* specified interval of time.
|
||||||
|
*
|
||||||
|
* This method does not check if the checkmarks have already been
|
||||||
|
* computed or not. If they have, then duplicate checkmarks will
|
||||||
|
* be stored, which is a bad thing.
|
||||||
|
*
|
||||||
|
* @param from timestamp for the beginning of the interval
|
||||||
|
* @param to timestamp for the end of the interval
|
||||||
|
*/
|
||||||
|
private synchronized void forceRecompute(long from, long to)
|
||||||
|
{
|
||||||
if (from > to) return;
|
if (from > to) return;
|
||||||
|
|
||||||
|
final long day = DateUtils.millisecondsInOneDay;
|
||||||
Frequency freq = habit.getFrequency();
|
Frequency freq = habit.getFrequency();
|
||||||
|
|
||||||
long fromExtended = from - (long) (freq.getDenominator()) * day;
|
long fromExtended = from - (long) (freq.getDenominator()) * day;
|
||||||
@@ -231,8 +266,8 @@ public abstract class CheckmarkList
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes and stores one checkmark for each day, since the first
|
* Computes and stores one checkmark for each day, since the first
|
||||||
* repetition until today. Days that already have a corresponding checkmark
|
* repetition of the habit until today. Days that already have a
|
||||||
* are skipped.
|
* corresponding checkmark are skipped.
|
||||||
*/
|
*/
|
||||||
protected final void computeAll()
|
protected final void computeAll()
|
||||||
{
|
{
|
||||||
@@ -245,8 +280,6 @@ public abstract class CheckmarkList
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns newest checkmark that has already been computed.
|
* Returns newest checkmark that has already been computed.
|
||||||
* <p>
|
|
||||||
* Ignores any checkmark that has timestamp in the future.
|
|
||||||
*
|
*
|
||||||
* @return newest checkmark already computed
|
* @return newest checkmark already computed
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -57,19 +57,6 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
*/
|
*/
|
||||||
public abstract void add(List<Score> scores);
|
public abstract void add(List<Score> scores);
|
||||||
|
|
||||||
public abstract List<Score> getAll();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the score that has the given timestamp.
|
|
||||||
* <p>
|
|
||||||
* If no such score exists, returns null.
|
|
||||||
*
|
|
||||||
* @param timestamp the timestamp to find.
|
|
||||||
* @return the score with given timestamp, or null if none exists.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public abstract Score getByTimestamp(long timestamp);
|
|
||||||
|
|
||||||
public ModelObservable getObservable()
|
public ModelObservable getObservable()
|
||||||
{
|
{
|
||||||
return observable;
|
return observable;
|
||||||
@@ -88,22 +75,23 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
/**
|
/**
|
||||||
* Returns the value of the score for a given day.
|
* Returns the value of the score for a given day.
|
||||||
* <p>
|
* <p>
|
||||||
* If there is no score at the given timestamp (for example, if the
|
* If the timestamp given happens before the first repetition of the habit
|
||||||
* timestamp given happens before the first repetition of the habit) then
|
* then returns zero.
|
||||||
* returns zero.
|
|
||||||
*
|
*
|
||||||
* @param timestamp the timestamp of a day
|
* @param timestamp the timestamp of a day
|
||||||
* @return score value for that day
|
* @return score value for that day
|
||||||
*/
|
*/
|
||||||
public final int getValue(long timestamp)
|
public final int getValue(long timestamp)
|
||||||
{
|
{
|
||||||
Score s = getByTimestamp(timestamp);
|
compute(timestamp, timestamp);
|
||||||
if (s != null) return s.getValue();
|
Score s = getComputedByTimestamp(timestamp);
|
||||||
return 0;
|
if(s == null) throw new IllegalStateException();
|
||||||
|
return s.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Score> groupBy(DateUtils.TruncateField field)
|
public List<Score> groupBy(DateUtils.TruncateField field)
|
||||||
{
|
{
|
||||||
|
computeAll();
|
||||||
HashMap<Long, ArrayList<Long>> groups = getGroupedValues(field);
|
HashMap<Long, ArrayList<Long>> groups = getGroupedValues(field);
|
||||||
List<Score> scores = groupsToAvgScores(groups);
|
List<Score> scores = groupsToAvgScores(groups);
|
||||||
Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1));
|
Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1));
|
||||||
@@ -122,9 +110,20 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
@Override
|
@Override
|
||||||
public Iterator<Score> iterator()
|
public Iterator<Score> iterator()
|
||||||
{
|
{
|
||||||
return getAll().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 abstract List<Score> toList();
|
||||||
|
|
||||||
public void writeCSV(Writer out) throws IOException
|
public void writeCSV(Writer out) throws IOException
|
||||||
{
|
{
|
||||||
computeAll();
|
computeAll();
|
||||||
@@ -140,16 +139,15 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes and saves the scores that are missing inside a given time
|
* Computes and stores one score for each day inside the given interval.
|
||||||
* interval.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Scores that have already been computed are skipped, therefore there is no
|
* Scores that have already been computed are skipped, therefore there is no
|
||||||
* harm in calling this function more times, or with larger intervals, than
|
* harm in calling this function more times, or with larger intervals, than
|
||||||
* strictly needed. The endpoints of the interval are included.
|
* strictly needed. The endpoints of the interval are included.
|
||||||
* <p>
|
* <p>
|
||||||
* This function assumes that there are no gaps on the scores. That is, if
|
* This method assumes the list of computed scores has no holes. That is, if
|
||||||
* the newest score has timestamp t, then every score with timestamp lower
|
* there is a score computed at time t1 and another at time t2, then every
|
||||||
* than t has already been computed.
|
* score between t1 and t2 is also computed.
|
||||||
*
|
*
|
||||||
* @param from timestamp of the beginning of the interval
|
* @param from timestamp of the beginning of the interval
|
||||||
* @param to timestamp of the end of the time interval
|
* @param to timestamp of the end of the time interval
|
||||||
@@ -157,34 +155,24 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
protected synchronized void compute(long from, long to)
|
protected synchronized void compute(long from, long to)
|
||||||
{
|
{
|
||||||
final long day = DateUtils.millisecondsInOneDay;
|
final long day = DateUtils.millisecondsInOneDay;
|
||||||
final double freq = habit.getFrequency().toDouble();
|
|
||||||
|
|
||||||
int newestValue = 0;
|
|
||||||
long newestTimestamp = 0;
|
|
||||||
|
|
||||||
Score newest = getNewestComputed();
|
Score newest = getNewestComputed();
|
||||||
if (newest != null)
|
Score oldest = getOldestComputed();
|
||||||
|
|
||||||
|
if (newest == null)
|
||||||
{
|
{
|
||||||
newestValue = newest.getValue();
|
Repetition oldestRep = habit.getRepetitions().getOldest();
|
||||||
newestTimestamp = newest.getTimestamp();
|
if (oldestRep != null)
|
||||||
|
from = Math.min(from, oldestRep.getTimestamp());
|
||||||
|
forceRecompute(from, to, 0);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (newestTimestamp > 0) from = newestTimestamp + day;
|
|
||||||
|
|
||||||
final int checkmarkValues[] = habit.getCheckmarks().getValues(from, to);
|
|
||||||
final long beginning = from;
|
|
||||||
|
|
||||||
int lastScore = newestValue;
|
|
||||||
List<Score> scores = new LinkedList<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < checkmarkValues.length; i++)
|
|
||||||
{
|
{
|
||||||
int value = checkmarkValues[checkmarkValues.length - i - 1];
|
if (oldest == null) throw new IllegalStateException();
|
||||||
lastScore = Score.compute(freq, lastScore, value);
|
forceRecompute(from, oldest.getTimestamp() - day, 0);
|
||||||
scores.add(new Score(beginning + day * i, lastScore));
|
forceRecompute(newest.getTimestamp() + day, to,
|
||||||
|
newest.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
add(scores);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -196,20 +184,66 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
Repetition oldestRep = habit.getRepetitions().getOldest();
|
Repetition oldestRep = habit.getRepetitions().getOldest();
|
||||||
if (oldestRep == null) return;
|
if (oldestRep == null) return;
|
||||||
|
|
||||||
long toTimestamp = DateUtils.getStartOfToday();
|
long today = DateUtils.getStartOfToday();
|
||||||
compute(oldestRep.getTimestamp(), toTimestamp);
|
compute(oldestRep.getTimestamp(), today);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the most recent score that has already been computed.
|
* Returns the score that has the given timestamp, if it has already been
|
||||||
* <p>
|
* computed. If that score has not been computed yet, returns null.
|
||||||
* If no score has been computed yet, returns null.
|
|
||||||
*
|
*
|
||||||
* @return the newest score computed, or null if none exist
|
* @param timestamp the timestamp of the score
|
||||||
|
* @return the score with given timestamp, or null not yet computed.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
protected abstract Score getComputedByTimestamp(long timestamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most recent score that has already been computed. If no score
|
||||||
|
* has been computed yet, returns null.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected abstract Score getNewestComputed();
|
protected abstract Score getNewestComputed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns oldest score already computed. If no score has been computed yet,
|
||||||
|
* returns null.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
protected abstract Score getOldestComputed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(long from, long to, int previousValue)
|
||||||
|
{
|
||||||
|
if(from > to) return;
|
||||||
|
|
||||||
|
final long day = DateUtils.millisecondsInOneDay;
|
||||||
|
|
||||||
|
final double freq = habit.getFrequency().toDouble();
|
||||||
|
final int checkmarkValues[] = habit.getCheckmarks().getValues(from, to);
|
||||||
|
|
||||||
|
List<Score> scores = new LinkedList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < checkmarkValues.length; i++)
|
||||||
|
{
|
||||||
|
int value = checkmarkValues[checkmarkValues.length - i - 1];
|
||||||
|
previousValue = Score.compute(freq, previousValue, value);
|
||||||
|
scores.add(new Score(from + day * i, previousValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
add(scores);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private HashMap<Long, ArrayList<Long>> getGroupedValues(DateUtils.TruncateField field)
|
private HashMap<Long, ArrayList<Long>> getGroupedValues(DateUtils.TruncateField field)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,22 +71,18 @@ public class MemoryCheckmarkList extends CheckmarkList
|
|||||||
list.removeAll(invalid);
|
list.removeAll(invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Checkmark getOldestComputed()
|
||||||
|
{
|
||||||
|
if(list.isEmpty()) return null;
|
||||||
|
return list.getLast();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Checkmark getNewestComputed()
|
protected Checkmark getNewestComputed()
|
||||||
{
|
{
|
||||||
long newestTimestamp = 0;
|
if(list.isEmpty()) return null;
|
||||||
Checkmark newestCheck = null;
|
return list.getFirst();
|
||||||
|
|
||||||
for (Checkmark c : list)
|
|
||||||
{
|
|
||||||
if (c.getTimestamp() > newestTimestamp)
|
|
||||||
{
|
|
||||||
newestCheck = c;
|
|
||||||
newestTimestamp = c.getTimestamp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newestCheck;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import java.util.*;
|
|||||||
|
|
||||||
public class MemoryScoreList extends ScoreList
|
public class MemoryScoreList extends ScoreList
|
||||||
{
|
{
|
||||||
List<Score> list;
|
LinkedList<Score> list;
|
||||||
|
|
||||||
public MemoryScoreList(Habit habit)
|
public MemoryScoreList(Habit habit)
|
||||||
{
|
{
|
||||||
@@ -35,6 +35,24 @@ public class MemoryScoreList extends ScoreList
|
|||||||
list = new LinkedList<>();
|
list = new LinkedList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(List<Score> scores)
|
||||||
|
{
|
||||||
|
list.addAll(scores);
|
||||||
|
Collections.sort(list,
|
||||||
|
(s1, s2) -> Long.signum(s2.getTimestamp() - s1.getTimestamp()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Score getComputedByTimestamp(long timestamp)
|
||||||
|
{
|
||||||
|
for (Score s : list)
|
||||||
|
if (s.getTimestamp() == timestamp) return s;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidateNewerThan(long timestamp)
|
public void invalidateNewerThan(long timestamp)
|
||||||
{
|
{
|
||||||
@@ -49,7 +67,7 @@ public class MemoryScoreList extends ScoreList
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public List<Score> getAll()
|
public List<Score> toList()
|
||||||
{
|
{
|
||||||
computeAll();
|
computeAll();
|
||||||
return new LinkedList<>(list);
|
return new LinkedList<>(list);
|
||||||
@@ -57,28 +75,17 @@ public class MemoryScoreList extends ScoreList
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Score getByTimestamp(long timestamp)
|
protected Score getNewestComputed()
|
||||||
{
|
{
|
||||||
computeAll();
|
if (list.isEmpty()) return null;
|
||||||
for (Score s : list)
|
return list.getFirst();
|
||||||
if (s.getTimestamp() == timestamp) return s;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(List<Score> scores)
|
|
||||||
{
|
|
||||||
list.addAll(scores);
|
|
||||||
Collections.sort(list,
|
|
||||||
(s1, s2) -> Long.signum(s2.getTimestamp() - s1.getTimestamp()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected Score getNewestComputed()
|
protected Score getOldestComputed()
|
||||||
{
|
{
|
||||||
if (list.isEmpty()) return null;
|
if (list.isEmpty()) return null;
|
||||||
return list.get(0);
|
return list.getLast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class SQLiteCheckmarkList extends CheckmarkList
|
|||||||
public List<Checkmark> getByInterval(long fromTimestamp, long toTimestamp)
|
public List<Checkmark> getByInterval(long fromTimestamp, long toTimestamp)
|
||||||
{
|
{
|
||||||
check(habit.getId());
|
check(habit.getId());
|
||||||
computeAll();
|
compute(fromTimestamp, toTimestamp);
|
||||||
|
|
||||||
String query = "select habit, timestamp, value " +
|
String query = "select habit, timestamp, value " +
|
||||||
"from checkmarks " +
|
"from checkmarks " +
|
||||||
@@ -119,7 +119,6 @@ public class SQLiteCheckmarkList extends CheckmarkList
|
|||||||
protected Checkmark getNewestComputed()
|
protected Checkmark getNewestComputed()
|
||||||
{
|
{
|
||||||
check(habit.getId());
|
check(habit.getId());
|
||||||
|
|
||||||
String query = "select habit, timestamp, value " +
|
String query = "select habit, timestamp, value " +
|
||||||
"from checkmarks " +
|
"from checkmarks " +
|
||||||
"where habit = ? " +
|
"where habit = ? " +
|
||||||
@@ -127,24 +126,42 @@ public class SQLiteCheckmarkList extends CheckmarkList
|
|||||||
"limit 1";
|
"limit 1";
|
||||||
|
|
||||||
String params[] = { Long.toString(habit.getId()) };
|
String params[] = { Long.toString(habit.getId()) };
|
||||||
|
return getSingleCheckmarkFromQuery(query, params);
|
||||||
|
}
|
||||||
|
|
||||||
CheckmarkRecord record = sqlite.querySingle(query, params);
|
@Override
|
||||||
if (record == null) return null;
|
protected Checkmark getOldestComputed()
|
||||||
record.habit = habitRecord;
|
{
|
||||||
return record.toCheckmark();
|
check(habit.getId());
|
||||||
|
String query = "select habit, timestamp, value " +
|
||||||
|
"from checkmarks " +
|
||||||
|
"where habit = ? " +
|
||||||
|
"order by timestamp asc " +
|
||||||
|
"limit 1";
|
||||||
|
|
||||||
|
String params[] = { Long.toString(habit.getId()) };
|
||||||
|
return getSingleCheckmarkFromQuery(query, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("null -> fail")
|
@Contract("null -> fail")
|
||||||
private void check(Long id)
|
private void check(Long id)
|
||||||
{
|
{
|
||||||
if (id == null) throw new RuntimeException("habit is not saved");
|
if (id == null) throw new RuntimeException("habit is not saved");
|
||||||
|
|
||||||
if (habitRecord != null) return;
|
if (habitRecord != null) return;
|
||||||
|
|
||||||
habitRecord = HabitRecord.get(id);
|
habitRecord = HabitRecord.get(id);
|
||||||
if (habitRecord == null) throw new RuntimeException("habit not found");
|
if (habitRecord == null) throw new RuntimeException("habit not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Checkmark getSingleCheckmarkFromQuery(String query, String params[])
|
||||||
|
{
|
||||||
|
CheckmarkRecord record = sqlite.querySingle(query, params);
|
||||||
|
if (record == null) return null;
|
||||||
|
record.habit = habitRecord;
|
||||||
|
return record.toCheckmark();
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private List<Checkmark> toCheckmarks(@NonNull List<CheckmarkRecord> records)
|
private List<Checkmark> toCheckmarks(@NonNull List<CheckmarkRecord> records)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -84,46 +84,20 @@ public class SQLiteScoreList extends ScoreList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@NonNull
|
|
||||||
public List<Score> getAll()
|
|
||||||
{
|
|
||||||
check(habit.getId());
|
|
||||||
computeAll();
|
|
||||||
|
|
||||||
String query = "select habit, timestamp, score from Score " +
|
|
||||||
"where habit = ? order by timestamp desc";
|
|
||||||
|
|
||||||
String params[] = {Long.toString(habit.getId())};
|
|
||||||
|
|
||||||
List<ScoreRecord> records = sqlite.query(query, params);
|
|
||||||
for (ScoreRecord record : records) record.habit = habitRecord;
|
|
||||||
|
|
||||||
List<Score> scores = new LinkedList<>();
|
|
||||||
for (ScoreRecord rec : records)
|
|
||||||
scores.add(rec.toScore());
|
|
||||||
|
|
||||||
return scores;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Score getByTimestamp(long timestamp)
|
public Score getComputedByTimestamp(long timestamp)
|
||||||
{
|
{
|
||||||
check(habit.getId());
|
check(habit.getId());
|
||||||
computeAll();
|
|
||||||
|
|
||||||
String query = "select habit, timestamp, score from Score " +
|
String query = "select habit, timestamp, score from Score " +
|
||||||
"where habit = ? and timestamp = ? " +
|
"where habit = ? and timestamp = ? " +
|
||||||
"order by timestamp desc";
|
"order by timestamp desc";
|
||||||
|
|
||||||
String params[] =
|
String params[] =
|
||||||
{Long.toString(habit.getId()), Long.toString(timestamp)};
|
{ Long.toString(habit.getId()), Long.toString(timestamp) };
|
||||||
|
|
||||||
ScoreRecord record = sqlite.querySingle(query, params);
|
return getScoreFromQuery(query, params);
|
||||||
if (record == null) return null;
|
|
||||||
record.habit = habitRecord;
|
|
||||||
return record.toScore();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -138,6 +112,28 @@ public class SQLiteScoreList extends ScoreList
|
|||||||
getObservable().notifyListeners();
|
getObservable().notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public List<Score> toList()
|
||||||
|
{
|
||||||
|
check(habit.getId());
|
||||||
|
computeAll();
|
||||||
|
|
||||||
|
String query = "select habit, timestamp, score from Score " +
|
||||||
|
"where habit = ? order by timestamp desc";
|
||||||
|
|
||||||
|
String params[] = { Long.toString(habit.getId()) };
|
||||||
|
|
||||||
|
List<ScoreRecord> records = sqlite.query(query, params);
|
||||||
|
for (ScoreRecord record : records) record.habit = habitRecord;
|
||||||
|
|
||||||
|
List<Score> scores = new LinkedList<>();
|
||||||
|
for (ScoreRecord rec : records)
|
||||||
|
scores.add(rec.toScore());
|
||||||
|
|
||||||
|
return scores;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected Score getNewestComputed()
|
protected Score getNewestComputed()
|
||||||
@@ -147,22 +143,38 @@ public class SQLiteScoreList extends ScoreList
|
|||||||
"where habit = ? order by timestamp desc " +
|
"where habit = ? order by timestamp desc " +
|
||||||
"limit 1";
|
"limit 1";
|
||||||
|
|
||||||
String params[] = {Long.toString(habit.getId())};
|
String params[] = { Long.toString(habit.getId()) };
|
||||||
|
return getScoreFromQuery(query, params);
|
||||||
|
}
|
||||||
|
|
||||||
ScoreRecord record = sqlite.querySingle(query, params);
|
@Nullable
|
||||||
if (record == null) return null;
|
@Override
|
||||||
record.habit = habitRecord;
|
protected Score getOldestComputed()
|
||||||
return record.toScore();
|
{
|
||||||
|
check(habit.getId());
|
||||||
|
String query = "select habit, timestamp, score from Score " +
|
||||||
|
"where habit = ? order by timestamp asc " +
|
||||||
|
"limit 1";
|
||||||
|
|
||||||
|
String params[] = { Long.toString(habit.getId()) };
|
||||||
|
return getScoreFromQuery(query, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("null -> fail")
|
@Contract("null -> fail")
|
||||||
private void check(Long id)
|
private void check(Long id)
|
||||||
{
|
{
|
||||||
if (id == null) throw new RuntimeException("habit is not saved");
|
if (id == null) throw new RuntimeException("habit is not saved");
|
||||||
|
if (habitRecord != null) return;
|
||||||
if(habitRecord != null) return;
|
|
||||||
|
|
||||||
habitRecord = HabitRecord.get(id);
|
habitRecord = HabitRecord.get(id);
|
||||||
if (habitRecord == null) throw new RuntimeException("habit not found");
|
if (habitRecord == null) throw new RuntimeException("habit not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Score getScoreFromQuery(String query, String[] params)
|
||||||
|
{
|
||||||
|
ScoreRecord record = sqlite.querySingle(query, params);
|
||||||
|
if (record == null) return null;
|
||||||
|
record.habit = habitRecord;
|
||||||
|
return record.toScore();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class ScoreWidget extends BaseWidget
|
|||||||
List<Score> scores;
|
List<Score> scores;
|
||||||
ScoreList scoreList = habit.getScores();
|
ScoreList scoreList = habit.getScores();
|
||||||
|
|
||||||
if (size == 1) scores = scoreList.getAll();
|
if (size == 1) scores = scoreList.toList();
|
||||||
else scores = scoreList.groupBy(ScoreCard.getTruncateField(size));
|
else scores = scoreList.groupBy(ScoreCard.getTruncateField(size));
|
||||||
|
|
||||||
int color = ColorUtils.getColor(getContext(), habit.getColor());
|
int color = ColorUtils.getColor(getContext(), habit.getColor());
|
||||||
|
|||||||
@@ -110,14 +110,17 @@ public class ScoreListTest extends BaseUnitTest
|
|||||||
3699107,
|
3699107,
|
||||||
2846927,
|
2846927,
|
||||||
1948077,
|
1948077,
|
||||||
1000000
|
1000000,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ScoreList scores = habit.getScores();
|
||||||
long current = DateUtils.getStartOfToday();
|
long current = DateUtils.getStartOfToday();
|
||||||
for (int expectedValue : expectedValues)
|
for (int expectedValue : expectedValues)
|
||||||
{
|
{
|
||||||
assertThat(habit.getScores().getValue(current),
|
assertThat(scores.getValue(current), equalTo(expectedValue));
|
||||||
equalTo(expectedValue));
|
|
||||||
current -= DateUtils.millisecondsInOneDay;
|
current -= DateUtils.millisecondsInOneDay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user