Replace RepetitionList by Entries

pull/699/head
Alinson S. Xavier 5 years ago
parent c904e22c0f
commit 3f74c77755

@ -75,7 +75,7 @@ public class HabitFixtures
81, 83, 89, 90, 91, 95, 102, 103, 108, 109, 120};
for (int mark : marks)
habit.getOriginalEntries().setValue(today.minus(mark), YES_MANUAL);
habit.getOriginalEntries().add(new Entry(today.minus(mark), YES_MANUAL));
return habit;
}
@ -109,7 +109,7 @@ public class HabitFixtures
582, 583, 584, 586, 589};
for (int mark : marks)
habit.getOriginalEntries().setValue(today.minus(mark), YES_MANUAL);
habit.getOriginalEntries().add(new Entry(today.minus(mark), YES_MANUAL));
return habit;
}
@ -128,7 +128,7 @@ public class HabitFixtures
Timestamp timestamp = DateUtils.getToday();
for (int value : LONG_NUMERICAL_HABIT_ENTRIES)
{
habit.getOriginalEntries().setValue(timestamp, value);
habit.getOriginalEntries().add(new Entry(timestamp, value));
timestamp = timestamp.minus(1);
}
@ -146,7 +146,7 @@ public class HabitFixtures
Timestamp timestamp = DateUtils.getToday();
for (boolean c : LONG_HABIT_ENTRIES)
{
if (c) habit.getOriginalEntries().setValue(timestamp, YES_MANUAL);
if (c) habit.getOriginalEntries().add(new Entry(timestamp, YES_MANUAL));
timestamp = timestamp.minus(1);
}

@ -46,7 +46,9 @@ public class FrequencyChartTest extends BaseViewTest
Habit habit = fixtures.createLongHabit();
view = new FrequencyChart(targetContext);
view.setFrequency(habit.getOriginalEntries().getWeekdayFrequency());
view.setFrequency(habit.getOriginalEntries().computeWeekdayFrequency(
habit.isNumerical()
));
view.setColor(PaletteUtilsKt.toFixedAndroidColor(habit.getColor()));
measureView(view, dpToPixels(300), dpToPixels(100));
}

@ -52,7 +52,9 @@ class FrequencyCardPresenter(
) {
fun present() = FrequencyCardViewModel(
color = habit.color,
frequency = habit.originalEntries.weekdayFrequency,
frequency = habit.originalEntries.computeWeekdayFrequency(
isNumerical = habit.isNumerical
),
firstWeekday = firstWeekday,
)
}

@ -25,6 +25,7 @@ import android.widget.*
import kotlinx.coroutines.*
import org.isoron.uhabits.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
import org.isoron.uhabits.core.utils.*
import org.isoron.uhabits.databinding.*
import org.isoron.uhabits.utils.*
@ -74,12 +75,16 @@ class OverviewCardPresenter(val habit: Habit) {
val scoreToday = scores.todayValue.toFloat()
val scoreLastMonth = scores.getValue(lastMonth).toFloat()
val scoreLastYear = scores.getValue(lastYear).toFloat()
val totalCount = habit.originalEntries.getKnown()
.filter { it.value == YES_MANUAL }
.count()
.toLong()
return@IO OverviewCardViewModel(
color = habit.color,
scoreToday = scoreToday,
scoreMonthDiff = scoreToday - scoreLastMonth,
scoreYearDiff = scoreToday - scoreLastYear,
totalCount = habit.originalEntries.totalCount,
totalCount = totalCount,
)
}
}

@ -44,7 +44,7 @@ class FrequencyWidget(
(widgetView.dataView as FrequencyChart).apply {
setFirstWeekday(firstWeekday)
setColor(habit.color.toThemedAndroidColor(context))
setFrequency(habit.originalEntries.weekdayFrequency)
setFrequency(habit.originalEntries.computeWeekdayFrequency(habit.isNumerical))
}
}

@ -52,8 +52,9 @@ public class CreateRepetitionCommand implements Command
@Override
public void execute()
{
RepetitionList checks = habit.getOriginalEntries();
checks.setValue(timestamp, value);
Entries checks = habit.getOriginalEntries();
checks.add(new Entry(timestamp, value));
habit.invalidateNewerThan(Timestamp.ZERO);
habitList.resort();
}

@ -95,7 +95,7 @@ public class HabitBullCSVImporter extends AbstractImporter
map.put(name, h);
}
h.getOriginalEntries().setValue(timestamp, YES_MANUAL);
h.getOriginalEntries().add(new Entry(timestamp, YES_MANUAL));
}
}
}

@ -244,10 +244,10 @@ public class HabitsCSVExporter
Timestamp newest = Timestamp.ZERO;
for (Habit h : selectedHabits)
{
if(h.getOriginalEntries().getOldest() == null || h.getOriginalEntries().getNewest() == null)
continue;
Timestamp currOld = h.getOriginalEntries().getOldest().getTimestamp();
Timestamp currNew = h.getOriginalEntries().getNewest().getTimestamp();
List<Entry> entries = h.getOriginalEntries().getKnown();
if (entries.isEmpty()) continue;
Timestamp currNew = entries.get(0).getTimestamp();
Timestamp currOld = entries.get(entries.size() - 1).getTimestamp();
oldest = currOld.isOlderThan(oldest) ? currOld : oldest;
newest = currNew.isNewerThan(newest) ? currNew : newest;
}

@ -101,10 +101,10 @@ public class LoopDBImporter extends AbstractImporter
habitsRepository = new Repository<>(HabitRecord.class, db);
entryRepository = new Repository<>(EntryRecord.class, db);
List<HabitRecord> records = habitsRepository.findAll("order by position");
for (HabitRecord habitRecord : records)
List<HabitRecord> habitRecords = habitsRepository.findAll("order by position");
for (HabitRecord habitRecord : habitRecords)
{
List<EntryRecord> reps =
List<EntryRecord> entryRecords =
entryRepository.findAll("where habit = ?",
habitRecord.id.toString());
@ -127,11 +127,11 @@ public class LoopDBImporter extends AbstractImporter
// Reload saved version of the habit
habit = habitList.getByUUID(habitRecord.uuid);
for (EntryRecord r : reps)
for (EntryRecord r : entryRecords)
{
Timestamp t = new Timestamp(r.timestamp);
Entry entry = habit.getOriginalEntries().getByTimestamp(t);
if (entry == null || entry.getValue() != r.value)
Entry existingEntry = habit.getOriginalEntries().get(t);
if (existingEntry.getValue() != r.value)
new CreateRepetitionCommand(habitList, habit, t, r.value).execute();
}
}

@ -167,7 +167,7 @@ public class RewireDBImporter extends AbstractImporter
GregorianCalendar cal = DateUtils.getStartOfTodayCalendar();
cal.set(year, month - 1, day);
habit.getOriginalEntries().setValue(new Timestamp(cal), YES_MANUAL);
habit.getOriginalEntries().add(new Entry(new Timestamp(cal), YES_MANUAL));
} while (c.moveToNext());
}
finally

@ -102,7 +102,7 @@ public class TickmateDBImporter extends AbstractImporter
GregorianCalendar cal = DateUtils.getStartOfTodayCalendar();
cal.set(year, month, day);
habit.getOriginalEntries().setValue(new Timestamp(cal), YES_MANUAL);
habit.getOriginalEntries().add(new Entry(new Timestamp(cal), YES_MANUAL));
} while (c.moveToNext());
}
finally

@ -23,6 +23,8 @@ import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
import org.isoron.uhabits.core.utils.*
import java.util.*
import kotlin.collections.HashMap
import kotlin.collections.set
import kotlin.math.*
@ -136,6 +138,42 @@ open class Entries {
entriesByTimestamp.clear()
}
/**
* Returns the total number of successful entries for each month, grouped by day of week.
* <p>
* The checkmarks are returned in a HashMap. The key is the timestamp for
* the first day of the month, at midnight (00:00). The value is an integer
* array with 7 entries. The first entry contains the total number of
* successful checkmarks during the specified month that occurred on a Saturday. The
* second entry corresponds to Sunday, and so on. If there are no
* successful checkmarks during a certain month, the value is null.
*
* @return total number of checkmarks by month versus day of week
*/
fun computeWeekdayFrequency(isNumerical: Boolean): HashMap<Timestamp, Array<Int>> {
val entries = getKnown()
val map = hashMapOf<Timestamp, Array<Int>>()
for ((originalTimestamp, value) in entries) {
val weekday = originalTimestamp.weekday
val truncatedTimestamp = Timestamp(originalTimestamp.toCalendar().apply {
set(Calendar.DAY_OF_MONTH, 1)
}.timeInMillis)
var list = map[truncatedTimestamp]
if (list == null) {
list = arrayOf(0, 0, 0, 0, 0, 0, 0)
map[truncatedTimestamp] = list
}
if (isNumerical) {
list[weekday] += value
} else if (value == YES_MANUAL) {
list[weekday] += 1
}
}
return map
}
data class Interval(val begin: Timestamp, val center: Timestamp, val end: Timestamp) {
val length: Int
get() = begin.daysUntil(end) + 1;

@ -182,8 +182,9 @@ public class EntryList
@NonNull
public synchronized final int[] getAllValues()
{
Entry oldestOriginal = habit.getOriginalEntries().getOldest();
if (oldestOriginal == null) return new int[0];
List<Entry> entries = habit.getOriginalEntries().getKnown();
if(entries.isEmpty()) return new int[0];
Entry oldestOriginal = entries.get(entries.size() - 1);
Timestamp fromTimestamp = oldestOriginal.getTimestamp();
Timestamp toTimestamp = DateUtils.getTodayWithOffset();
@ -365,16 +366,18 @@ public class EntryList
if (newest != null && newest.getTimestamp().equals(today)) return;
invalidateNewerThan(Timestamp.ZERO);
Entry oldestRep = habit.getOriginalEntries().getOldest();
if (oldestRep == null) return;
final Timestamp from = oldestRep.getTimestamp();
List<Entry> entries = habit.getOriginalEntries().getKnown();
if(entries.isEmpty()) return;
final Timestamp from = entries.get(entries.size() - 1).getTimestamp();
if (from.isNewerThan(today)) return;
Entry reps[] = habit
.getOriginalEntries()
.getByInterval(from, today)
.toArray(new Entry[0]);
Entry[] reps = entries.stream().filter(e ->
!e.getTimestamp().isOlderThan(from) && !e.getTimestamp().isNewerThan(today)
).toArray(Entry[]::new);
List<Entry> repsAsList = Arrays.asList(reps);
Collections.reverse(repsAsList);
reps = repsAsList.toArray(reps);
if (habit.isNumerical()) computeNumerical(reps);
else computeYesNo(reps);
@ -425,8 +428,9 @@ public class EntryList
public List<Entry> getAll()
{
Entry oldest = habit.getOriginalEntries().getOldest();
if (oldest == null) return new ArrayList<>();
List<Entry> entries = habit.getOriginalEntries().getKnown();
if(entries.isEmpty()) return new ArrayList<>();
Entry oldest = entries.get(entries.size() - 1);
return getByInterval(oldest.getTimestamp(), DateUtils.getTodayWithOffset());
}
@ -456,6 +460,9 @@ public class EntryList
Interval(Timestamp begin, Timestamp center, Timestamp end)
{
if(begin.isNewerThan(center)) throw new IllegalArgumentException();
if(center.isNewerThan(end)) throw new IllegalArgumentException();
this.begin = begin;
this.center = center;
this.end = end;

@ -34,7 +34,7 @@ data class Habit(
var unit: String = "",
var uuid: String? = null,
val computedEntries: EntryList,
val originalEntries: RepetitionList,
val originalEntries: Entries,
val scores: ScoreList,
val streaks: StreakList,
) {

@ -29,25 +29,23 @@ interface ModelFactory {
fun buildHabit(): Habit {
val computedEntries = buildEntryList()
val originalEntries = buildRepetitionList()
val scores = buildScoreList()
val streaks = buildStreakList()
val habit = Habit(
computedEntries = computedEntries,
originalEntries = originalEntries,
scores = scores,
streaks = streaks,
originalEntries = buildOriginalEntries(),
)
computedEntries.setHabit(habit)
originalEntries.setHabit(habit)
scores.setHabit(habit)
streaks.setHabit(habit)
return habit
}
fun buildOriginalEntries(): Entries
fun buildEntryList(): EntryList
fun buildHabitList(): HabitList
fun buildRepetitionList(): RepetitionList
fun buildScoreList(): ScoreList
fun buildStreakList(): StreakList
fun buildHabitListRepository(): Repository<HabitRecord>

@ -1,243 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.models;
import androidx.annotation.*;
import org.isoron.uhabits.core.utils.*;
import java.util.*;
public class RepetitionList
{
protected Habit habit;
private final ArrayList<Entry> list = new ArrayList<>();
public void setHabit(Habit habit)
{
this.habit = habit;
}
/**
* Adds a checkmark to the list.
* <p>
* Any implementation of this method must call observable.notifyListeners()
* after the checkmark has been added.
*
* @param entry the checkmark to be added.
*/
public void add(Entry entry)
{
list.add(entry);
}
/**
* Returns the list of checkmarks that happened within the given time
* interval.
* <p>
* The list is sorted by timestamp in increasing order. That is, the first
* element corresponds to oldest timestamp, while the last element
* corresponds to the newest. The endpoints of the interval are included.
*
* @param fromTimestamp timestamp of the beginning of the interval
* @param toTimestamp timestamp of the end of the interval
* @return list of checkmarks within given time interval
*/
public List<Entry> getByInterval(Timestamp fromTimestamp, Timestamp toTimestamp)
{
ArrayList<Entry> filtered = new ArrayList<>();
for (Entry r : list)
{
Timestamp t = r.getTimestamp();
if (t.isOlderThan(fromTimestamp) || t.isNewerThan(toTimestamp)) continue;
filtered.add(r);
}
Collections.sort(filtered,
(r1, r2) -> r1.getTimestamp().compareTo(r2.getTimestamp()));
return filtered;
}
/**
* Returns the checkmark that has the given timestamp, or null if none
* exists.
*
* @param timestamp the checkmark timestamp.
* @return the checkmark that has the given timestamp.
*/
@Nullable
public Entry getByTimestamp(Timestamp timestamp)
{
for (Entry r : list)
if (r.getTimestamp().equals(timestamp)) return r;
return null;
}
/**
* If a checkmark with the given timestamp exists, return its value. Otherwise, returns
* Checkmark.NO for boolean habits and zero for numerical habits.
*/
@NonNull
public int getValue(Timestamp timestamp)
{
Entry check = getByTimestamp(timestamp);
if (check == null) return Entry.UNKNOWN;
return check.getValue();
}
/**
* Returns the oldest checkmark in the list.
* <p>
* If the list is empty, returns null. Repetitions in the future are
* discarded.
*
* @return oldest checkmark in the list, or null if list is empty.
*/
@Nullable
public Entry getOldest()
{
Timestamp oldestTimestamp = Timestamp.ZERO.plus(1000000);
Entry oldestRep = null;
for (Entry rep : list)
{
if (rep.getTimestamp().isOlderThan(oldestTimestamp))
{
oldestRep = rep;
oldestTimestamp = rep.getTimestamp();
}
}
return oldestRep;
}
/**
* Returns the newest checkmark in the list.
* <p>
* If the list is empty, returns null. Repetitions in the past are
* discarded.
*
* @return newest checkmark in the list, or null if list is empty.
*/
@Nullable
public Entry getNewest()
{
Timestamp newestTimestamp = Timestamp.ZERO;
Entry newestRep = null;
for (Entry rep : list)
{
if (rep.getTimestamp().isNewerThan(newestTimestamp))
{
newestRep = rep;
newestTimestamp = rep.getTimestamp();
}
}
return newestRep;
}
/**
* Returns the total number of successful checkmarks for each month, from the first
* checkmark until today, grouped by day of week.
* <p>
* The checkmarks are returned in a HashMap. The key is the timestamp for
* the first day of the month, at midnight (00:00). The value is an integer
* array with 7 entries. The first entry contains the total number of
* successful checkmarks during the specified month that occurred on a Saturday. The
* second entry corresponds to Sunday, and so on. If there are no
* successful checkmarks during a certain month, the value is null.
*
* @return total number of checkmarks by month versus day of week
*/
@NonNull
public HashMap<Timestamp, Integer[]> getWeekdayFrequency()
{
List<Entry> entries =
getByInterval(Timestamp.ZERO, DateUtils.getTodayWithOffset());
HashMap<Timestamp, Integer[]> map = new HashMap<>();
for (Entry e : entries)
{
if (!habit.isNumerical() && e.getValue() != Entry.YES_MANUAL)
continue;
Calendar date = e.getTimestamp().toCalendar();
int weekday = e.getTimestamp().getWeekday();
date.set(Calendar.DAY_OF_MONTH, 1);
Timestamp timestamp = new Timestamp(date.getTimeInMillis());
Integer[] list = map.get(timestamp);
if (list == null)
{
list = new Integer[7];
Arrays.fill(list, 0);
map.put(timestamp, list);
}
list[weekday]++;
}
return map;
}
/**
* Removes a given checkmark from the list.
* <p>
* If the list does not contain the checkmark, it is unchanged.
* <p>
* Any implementation of this method must call observable.notifyListeners()
* after the checkmark has been added.
*
* @param entry the checkmark to be removed
*/
public void remove(@NonNull Entry entry)
{
list.remove(entry);
}
public long getTotalCount()
{
int count = 0;
for (Entry rep : list)
if (rep.getValue() == Entry.YES_MANUAL)
count++;
return count;
}
public void setValue(Timestamp timestamp, int value)
{
Entry check = getByTimestamp(timestamp);
if (check != null) remove(check);
add(new Entry(timestamp, value));
habit.invalidateNewerThan(timestamp);
}
public void removeAll()
{
list.clear();
}
}

@ -192,9 +192,11 @@ public abstract class ScoreList implements Iterable<Score>
if (newestComputed == null)
{
Entry oldest = habit.getOriginalEntries().getOldest();
if (oldest != null) from =
Timestamp.oldest(from, oldest.getTimestamp());
List<Entry> entries = habit.getOriginalEntries().getKnown();
if (!entries.isEmpty())
from = Timestamp.oldest(
from,
entries.get(entries.size() - 1).getTimestamp());
forceRecompute(from, to, 0);
}
else
@ -212,8 +214,9 @@ public abstract class ScoreList implements Iterable<Score>
*/
protected void computeAll()
{
Entry oldest = habit.getOriginalEntries().getOldest();
if (oldest == null) return;
List<Entry> entries = habit.getOriginalEntries().getKnown();
if(entries.isEmpty()) return;
Entry oldest = entries.get(entries.size() - 1);
Timestamp today = DateUtils.getTodayWithOffset();
compute(oldest.getTimestamp(), today);

@ -112,9 +112,9 @@ public abstract class StreakList
Streak newestStreak = getNewestComputed();
if (newestStreak != null) return newestStreak.getStart();
Entry oldestOriginal = habit.getOriginalEntries().getOldest();
if (oldestOriginal != null) return oldestOriginal.getTimestamp();
return null;
List<Entry> entries = habit.getOriginalEntries().getKnown();
if(entries.isEmpty()) return null;
return entries.get(entries.size() - 1).getTimestamp();
}
/**

@ -21,9 +21,9 @@ package org.isoron.uhabits.core.models.memory
import org.isoron.uhabits.core.models.*
class MemoryModelFactory : ModelFactory {
override fun buildOriginalEntries() = Entries()
override fun buildEntryList() = EntryList()
override fun buildHabitList() = MemoryHabitList()
override fun buildRepetitionList() = RepetitionList()
override fun buildScoreList() = MemoryScoreList()
override fun buildStreakList() = MemoryStreakList()
override fun buildHabitListRepository() = throw NotImplementedError()

@ -33,10 +33,9 @@ class SQLModelFactory
@Inject constructor(
val database: Database,
) : ModelFactory {
override fun buildOriginalEntries() = SQLiteEntries(database)
override fun buildEntryList() = EntryList()
override fun buildHabitList() = SQLiteHabitList(this)
override fun buildRepetitionList() = SQLiteRepetitionList(this)
override fun buildScoreList() = MemoryScoreList()
override fun buildStreakList() = MemoryStreakList()

@ -81,6 +81,8 @@ class SQLiteEntries(database: Database) : Entries() {
}
override fun clear() {
throw UnsupportedOperationException()
super.clear()
repository.execSQL("delete from repetitions where habit = ?",
habitId.toString())
}
}

@ -37,8 +37,6 @@ import javax.inject.*;
*/
public class SQLiteHabitList extends HabitList
{
private static SQLiteHabitList instance;
@NonNull
private final Repository<HabitRecord> repository;
@ -76,6 +74,7 @@ public class SQLiteHabitList extends HabitList
Habit h = modelFactory.buildHabit();
rec.copyTo(h);
((SQLiteEntries) h.getOriginalEntries()).setHabitId(h.getId());
list.add(h);
}
@ -92,6 +91,7 @@ public class SQLiteHabitList extends HabitList
record.copyFrom(habit);
repository.save(record);
habit.setId(record.id);
((SQLiteEntries) habit.getOriginalEntries()).setHabitId(record.id);
list.add(habit);
getObservable().notifyListeners();
@ -201,7 +201,7 @@ public class SQLiteHabitList extends HabitList
if (record == null) throw new RuntimeException("habit not in database");
repository.executeAsTransaction(() ->
{
habit.getOriginalEntries().removeAll();
habit.getOriginalEntries().clear();
repository.remove(record);
});

@ -1,135 +0,0 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.models.sqlite;
import androidx.annotation.Nullable;
import androidx.annotation.*;
import org.isoron.uhabits.core.database.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.jetbrains.annotations.*;
import java.util.*;
/**
* Implementation of a {@link RepetitionList} that is backed by SQLite.
*/
public class SQLiteRepetitionList extends RepetitionList
{
private final Repository<EntryRecord> repository;
private boolean loaded = false;
public SQLiteRepetitionList(@NonNull ModelFactory modelFactory)
{
repository = modelFactory.buildRepetitionListRepository();
}
private void loadRecords()
{
if (loaded) return;
loaded = true;
check(habit.getId());
List<EntryRecord> records =
repository.findAll("where habit = ? order by timestamp",
habit.getId().toString());
for (EntryRecord rec : records)
super.add(rec.toEntry());
}
@Override
public void add(Entry entry)
{
loadRecords();
super.add(entry);
check(habit.getId());
EntryRecord record = new EntryRecord();
record.habitId = habit.getId();
record.copyFrom(entry);
repository.save(record);
}
@Override
public List<Entry> getByInterval(Timestamp timeFrom, Timestamp timeTo)
{
loadRecords();
return super.getByInterval(timeFrom, timeTo);
}
@Override
@Nullable
public Entry getByTimestamp(Timestamp timestamp)
{
loadRecords();
return super.getByTimestamp(timestamp);
}
@Override
public Entry getOldest()
{
loadRecords();
return super.getOldest();
}
@Override
public Entry getNewest()
{
loadRecords();
return super.getNewest();
}
@Override
public void remove(@NonNull Entry entry)
{
loadRecords();
super.remove(entry);
check(habit.getId());
repository.execSQL(
"delete from repetitions where habit = ? and timestamp = ?",
habit.getId(), entry.getTimestamp().getUnixTime());
}
public void removeAll()
{
loadRecords();
super.removeAll();
check(habit.getId());
repository.execSQL("delete from repetitions where habit = ?",
habit.getId());
}
@Override
public long getTotalCount()
{
loadRecords();
return super.getTotalCount();
}
@Contract("null -> fail")
private void check(Long value)
{
if (value == null) throw new RuntimeException("null check failed");
}
}

@ -65,7 +65,7 @@ public class HabitFixtures
81, 83, 89, 90, 91, 95, 102, 103, 108, 109, 120};
for (int mark : marks)
habit.getOriginalEntries().setValue(today.minus(mark), YES_MANUAL);
habit.getOriginalEntries().add(new Entry(today.minus(mark), YES_MANUAL));
return habit;
}
@ -89,7 +89,7 @@ public class HabitFixtures
for (int i = 0; i < times.length; i++)
{
Timestamp timestamp = today.minus(times[i]);
habit.getOriginalEntries().setValue(timestamp, values[i]);
habit.getOriginalEntries().add(new Entry(timestamp, values[i]));
}
return habit;
@ -125,7 +125,7 @@ public class HabitFixtures
for (int i = 0; i < times.length; i++)
{
Timestamp timestamp = reference.minus(times[i]);
habit.getOriginalEntries().setValue(timestamp, values[i]);
habit.getOriginalEntries().add(new Entry(timestamp, values[i]));
}
return habit;
@ -144,7 +144,7 @@ public class HabitFixtures
{
int value = NO;
if (c) value = YES_MANUAL;
habit.getOriginalEntries().setValue(timestamp, value);
habit.getOriginalEntries().add(new Entry(timestamp, value));
timestamp = timestamp.minus(1);
}
@ -153,7 +153,7 @@ public class HabitFixtures
private void saveIfSQLite(Habit habit)
{
if (!(habit.getOriginalEntries() instanceof SQLiteRepetitionList)) return;
if (!(habit.getOriginalEntries() instanceof SQLiteEntries)) return;
habitList.add(habit);
}
}

@ -58,14 +58,14 @@ class ShowHabitMenuBehavior(
fun onRandomize() {
val random = Random()
habit.originalEntries.removeAll()
habit.originalEntries.clear()
var strength = 50.0
for (i in 0 until 365 * 5) {
if (i % 7 == 0) strength = Math.max(0.0, Math.min(100.0, strength + 10 * random.nextGaussian()))
if (random.nextInt(100) > strength) continue
var value = Entry.YES_MANUAL
if (habit.isNumerical) value = (1000 + 250 * random.nextGaussian() * strength / 100).toInt() * 1000
habit.originalEntries.setValue(DateUtils.getToday().minus(i), value)
habit.originalEntries.add(Entry(DateUtils.getToday().minus(i), value))
}
habit.invalidateNewerThan(Timestamp.ZERO)
screen.refresh()

@ -70,7 +70,7 @@ public class WidgetBehavior
public void onToggleRepetition(@NonNull Habit habit, Timestamp timestamp)
{
int currentValue = habit.getOriginalEntries().getValue(timestamp);
int currentValue = habit.getOriginalEntries().get(timestamp).getValue();
int newValue;
if(preferences.isSkipEnabled())
newValue = Entry.Companion.nextToggleValueWithSkip(currentValue);

@ -51,13 +51,12 @@ public class CreateRepetitionCommandTest extends BaseUnitTest
@Test
public void testExecute()
{
RepetitionList originalEntries = habit.getOriginalEntries();
Entry entry = originalEntries.getByTimestamp(today);
assertNotNull(entry);
Entries originalEntries = habit.getOriginalEntries();
Entry entry = originalEntries.get(today);
assertEquals(YES_MANUAL, entry.getValue());
command.execute();
entry = originalEntries.getByTimestamp(today);
assertNotNull(entry);
entry = originalEntries.get(today);
assertEquals(100, entry.getValue());
}
}

@ -123,7 +123,7 @@ public class ImportTest extends BaseUnitTest
GregorianCalendar date = DateUtils.getStartOfTodayCalendar();
date.set(year, month - 1, day);
Timestamp timestamp = new Timestamp(date);
return h.getOriginalEntries().getValue(timestamp) == YES_MANUAL;
return h.getOriginalEntries().get(timestamp).getValue() == YES_MANUAL;
}
private void importFromFile(String assetFilename) throws IOException

@ -297,6 +297,45 @@ class EntriesTest {
assertThat(actual, equalTo(expected))
}
@Test
fun testWeekdayFrequency() {
val entries = Entries()
val random = Random(123L)
val weekdayCount = Array(12) { Array(7) { 0 } }
val monthCount = Array(12) { 0 }
val day = DateUtils.getStartOfTodayCalendar()
// Add repetitions randomly from January to December
day.set(2015, Calendar.JANUARY, 1, 0, 0, 0)
for (i in 0..364) {
if (random.nextBoolean()) {
val month = day[Calendar.MONTH]
val week = day[Calendar.DAY_OF_WEEK] % 7
// Leave the month of March empty, to check that it returns null
if (month == Calendar.MARCH) continue
entries.add(Entry(Timestamp(day), YES_MANUAL))
weekdayCount[month][week]++
monthCount[month]++
}
day.add(Calendar.DAY_OF_YEAR, 1)
}
val freq = entries.computeWeekdayFrequency(isNumerical = false)
// Repetitions should be counted correctly
for (month in 0..11) {
day.set(2015, month, 1, 0, 0, 0)
val actualCount = freq[Timestamp(day)]
if (monthCount[month] == 0) {
assertThat(actualCount, equalTo(null))
} else {
assertThat(actualCount, equalTo(weekdayCount[month]))
}
}
}
fun day(offset: Int) = DateUtils.getToday().minus(offset)
}

@ -1,468 +0,0 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.models;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import java.io.*;
import java.util.*;
import nl.jqno.equalsverifier.*;
import static java.util.Calendar.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
import static org.isoron.uhabits.core.models.Entry.*;
import static org.isoron.uhabits.core.utils.DateUtils.TruncateField.MONTH;
import static org.isoron.uhabits.core.utils.DateUtils.TruncateField.QUARTER;
import static org.isoron.uhabits.core.utils.DateUtils.TruncateField.YEAR;
public class EntryListTest extends BaseUnitTest
{
private long dayLength;
private Timestamp today;
private Habit nonDailyHabit;
private Habit emptyHabit;
private Habit numericalHabit;
@Override
public void setUp() throws Exception
{
super.setUp();
nonDailyHabit = fixtures.createShortHabit();
habitList.add(nonDailyHabit);
emptyHabit = fixtures.createEmptyHabit();
habitList.add(emptyHabit);
numericalHabit = fixtures.createNumericalHabit();
habitList.add(numericalHabit);
today = DateUtils.getToday();
}
@Test
public void test_buildCheckmarksFromIntervals_1() throws Exception
{
Entry entries[] = new Entry[]{
new Entry(day(10), YES_MANUAL),
new Entry(day(5), YES_MANUAL),
new Entry(day(2), YES_MANUAL),
new Entry(day(1), YES_MANUAL),
};
ArrayList<EntryList.Interval> intervals = new ArrayList<>();
intervals.add(new EntryList.Interval(day(10), day(8), day(8)));
intervals.add(new EntryList.Interval(day(6), day(5), day(4)));
intervals.add(new EntryList.Interval(day(2), day(2), day(1)));
List<Entry> expected = new ArrayList<>();
expected.add(new Entry(day(0), UNKNOWN));
expected.add(new Entry(day(1), YES_MANUAL));
expected.add(new Entry(day(2), YES_MANUAL));
expected.add(new Entry(day(3), UNKNOWN));
expected.add(new Entry(day(4), YES_AUTO));
expected.add(new Entry(day(5), YES_MANUAL));
expected.add(new Entry(day(6), YES_AUTO));
expected.add(new Entry(day(7), UNKNOWN));
expected.add(new Entry(day(8), YES_AUTO));
expected.add(new Entry(day(9), YES_AUTO));
expected.add(new Entry(day(10), YES_MANUAL));
List<Entry> actual =
EntryList.buildEntriesFromInterval(entries, intervals);
assertThat(actual, equalTo(expected));
}
@Test
public void test_buildCheckmarksFromIntervals_2() throws Exception
{
Entry entries[] = new Entry[]{
new Entry(day(0), YES_MANUAL),
};
ArrayList<EntryList.Interval> intervals = new ArrayList<>();
intervals.add(new EntryList.Interval(day(0), day(0), day(-10)));
List<Entry> expected = new ArrayList<>();
expected.add(new Entry(day(0), YES_MANUAL));
List<Entry> actual =
EntryList.buildEntriesFromInterval(entries, intervals);
assertThat(actual, equalTo(expected));
}
@Test
public void test_buildIntervals_1() throws Exception
{
Entry entries[] = new Entry[]{
new Entry(day(23), YES_MANUAL),
new Entry(day(18), YES_MANUAL),
new Entry(day(8), YES_MANUAL),
};
ArrayList<EntryList.Interval> expected = new ArrayList<>();
expected.add(new EntryList.Interval(day(23), day(23), day(17)));
expected.add(new EntryList.Interval(day(18), day(18), day(12)));
expected.add(new EntryList.Interval(day(8), day(8), day(2)));
ArrayList<EntryList.Interval> actual;
actual = EntryList.buildIntervals(Frequency.WEEKLY, entries);
assertThat(actual, equalTo(expected));
}
@Test
public void test_buildIntervals_2() throws Exception
{
Entry entries[] = new Entry[]{
new Entry(day(23), YES_MANUAL),
new Entry(day(18), YES_MANUAL),
new Entry(day(8), YES_MANUAL),
};
ArrayList<EntryList.Interval> expected = new ArrayList<>();
expected.add(new EntryList.Interval(day(23), day(23), day(23)));
expected.add(new EntryList.Interval(day(18), day(18), day(18)));
expected.add(new EntryList.Interval(day(8), day(8), day(8)));
ArrayList<EntryList.Interval> actual;
actual = EntryList.buildIntervals(Frequency.DAILY, entries);
assertThat(actual, equalTo(expected));
}
@Test
public void test_buildIntervals_3() throws Exception
{
Entry entries[] = new Entry[]{
new Entry(day(23), YES_MANUAL),
new Entry(day(22), YES_MANUAL),
new Entry(day(18), YES_MANUAL),
new Entry(day(15), YES_MANUAL),
new Entry(day(8), YES_MANUAL),
};
ArrayList<EntryList.Interval> expected = new ArrayList<>();
expected.add(new EntryList.Interval(day(23), day(22), day(17)));
expected.add(new EntryList.Interval(day(22), day(18), day(16)));
expected.add(new EntryList.Interval(day(18), day(15), day(12)));
ArrayList<EntryList.Interval> actual;
actual =
EntryList.buildIntervals(Frequency.TWO_TIMES_PER_WEEK, entries);
assertThat(actual, equalTo(expected));
}
@Test
public void test_buildIntervals_4() throws Exception
{
Entry[] entries = new Entry[]{
new Entry(day(30), YES_MANUAL),
new Entry(day(20), SKIP),
new Entry(day(10), YES_MANUAL),
};
ArrayList<EntryList.Interval> expected = new ArrayList<>();
expected.add(new EntryList.Interval(day(30), day(30), day(28)));
expected.add(new EntryList.Interval(day(10), day(10), day(8)));
ArrayList<EntryList.Interval> actual;
actual = EntryList.buildIntervals(new Frequency(1, 3), entries);
assertThat(actual, equalTo(expected));
}
@Test
public void test_getAllValues_moveBackwardsInTime()
{
travelInTime(-3);
int[] expectedValues = {
YES_MANUAL,
YES_MANUAL,
YES_MANUAL,
YES_AUTO,
YES_AUTO,
YES_MANUAL,
YES_MANUAL
};
int[] actualValues = nonDailyHabit.getComputedEntries().getAllValues();
assertThat(actualValues, equalTo(expectedValues));
}
@Test
public void test_getAllValues_moveForwardInTime()
{
travelInTime(3);
int[] expectedValues = {
UNKNOWN,
UNKNOWN,
UNKNOWN,
YES_MANUAL,
NO,
YES_AUTO,
YES_MANUAL,
YES_MANUAL,
YES_MANUAL,
YES_AUTO,
YES_AUTO,
YES_MANUAL,
YES_MANUAL
};
int[] actualValues = nonDailyHabit.getComputedEntries().getAllValues();
assertThat(actualValues, equalTo(expectedValues));
}
@Test
public void test_getAllValues_withEmptyHabit()
{
int[] expectedValues = new int[0];
int[] actualValues = emptyHabit.getComputedEntries().getAllValues();
assertThat(actualValues, equalTo(expectedValues));
}
@Test
public void test_getAllValues_withNonDailyHabit()
{
int[] expectedValues = {
YES_MANUAL,
NO,
YES_AUTO,
YES_MANUAL,
YES_MANUAL,
YES_MANUAL,
YES_AUTO,
YES_AUTO,
YES_MANUAL,
YES_MANUAL
};
int[] actualValues = nonDailyHabit.getComputedEntries().getAllValues();
assertThat(actualValues, equalTo(expectedValues));
}
@Test
public void test_getByInterval_withNumericalHabits() throws Exception
{
EntryList entries = numericalHabit.getComputedEntries();
List<Entry> expected =
Arrays.asList(new Entry(day(1), 200), new Entry(day(2), 0),
new Entry(day(3), 300), new Entry(day(4), 0),
new Entry(day(5), 400));
List<Entry> actual = entries.getByInterval(day(5), day(1));
assertThat(actual, equalTo(expected));
}
@Test
public void test_getTodayValue()
{
EntryList entries = nonDailyHabit.getComputedEntries();
travelInTime(-1);
assertThat(entries.getTodayValue(), equalTo(NO));
travelInTime(0);
assertThat(entries.getTodayValue(), equalTo(YES_MANUAL));
travelInTime(1);
assertThat(entries.getTodayValue(), equalTo(UNKNOWN));
}
@Test
public void test_getValues_withInvalidInterval()
{
int values[] = nonDailyHabit
.getComputedEntries()
.getValues(new Timestamp(0L).plus(100), new Timestamp(0L));
assertThat(values, equalTo(new int[0]));
}
@Test
public void test_getValues_withValidInterval()
{
Timestamp from = today.minus(15);
Timestamp to = today.minus(5);
int[] expectedValues = {
YES_MANUAL,
YES_AUTO,
YES_AUTO,
YES_MANUAL,
YES_MANUAL,
UNKNOWN,
UNKNOWN,
UNKNOWN,
UNKNOWN,
UNKNOWN,
UNKNOWN
};
int[] actualValues = nonDailyHabit.getComputedEntries().getValues(from, to);
assertThat(actualValues, equalTo(expectedValues));
}
@Test
public void test_snapIntervalsTogether_1() throws Exception
{
ArrayList<EntryList.Interval> original = new ArrayList<>();
original.add(new EntryList.Interval(day(27), day(27), day(21)));
original.add(new EntryList.Interval(day(20), day(20), day(14)));
original.add(new EntryList.Interval(day(12), day(12), day(6)));
original.add(new EntryList.Interval(day(8), day(8), day(2)));
ArrayList<EntryList.Interval> expected = new ArrayList<>();
expected.add(new EntryList.Interval(day(29), day(27), day(23)));
expected.add(new EntryList.Interval(day(22), day(20), day(16)));
expected.add(new EntryList.Interval(day(15), day(12), day(9)));
expected.add(new EntryList.Interval(day(8), day(8), day(2)));
EntryList.snapIntervalsTogether(original);
assertThat(original, equalTo(expected));
}
@Test
public void test_snapIntervalsTogether_2() throws Exception
{
ArrayList<EntryList.Interval> original = new ArrayList<>();
original.add(new EntryList.Interval(day(11), day(8), day(5)));
original.add(new EntryList.Interval(day(6), day(4), day(0)));
ArrayList<EntryList.Interval> expected = new ArrayList<>();
expected.add(new EntryList.Interval(day(13), day(8), day(7)));
expected.add(new EntryList.Interval(day(6), day(4), day(0)));
EntryList.snapIntervalsTogether(original);
assertThat(original, equalTo(expected));
}
@Test
public void test_writeCSV() throws IOException
{
String expectedCSV = "2015-01-25,2\n2015-01-24,0\n2015-01-23,1\n" +
"2015-01-22,2\n2015-01-21,2\n2015-01-20,2\n" +
"2015-01-19,1\n2015-01-18,1\n2015-01-17,2\n" +
"2015-01-16,2\n";
StringWriter writer = new StringWriter();
nonDailyHabit.getComputedEntries().writeCSV(writer);
assertThat(writer.toString(), equalTo(expectedCSV));
}
private Timestamp day(int offset)
{
return DateUtils.getToday().minus(offset);
}
private void travelInTime(int days)
{
DateUtils.setFixedLocalTime(
FIXED_LOCAL_TIME + days * Timestamp.DAY_LENGTH);
}
@Test
public void testEquals() throws Exception
{
EqualsVerifier.forClass(Entry.class).verify();
EqualsVerifier.forClass(Timestamp.class).verify();
EqualsVerifier.forClass(EntryList.Interval.class).verify();
}
@Test
public void testGroupBy() throws Exception
{
Habit habit = fixtures.createLongNumericalHabit(timestamp(2014, JUNE, 1));
EntryList entries = habit.getComputedEntries();
List<Entry> byMonth = entries.groupBy(MONTH, Calendar.SATURDAY);
assertThat(byMonth.size(), equalTo(25)); // from 2013-01-01 to 2015-01-01
assertThat(byMonth.get(0), equalTo(new Entry(timestamp(2015, JANUARY, 1), 0)));
assertThat(byMonth.get(6), equalTo(new Entry(timestamp(2014, JULY, 1), 0)));
assertThat(byMonth.get(12), equalTo(new Entry(timestamp(2014, JANUARY, 1), 1706)));
assertThat(byMonth.get(18), equalTo(new Entry(timestamp(2013, JULY, 1), 1379)));
List<Entry> byQuarter = entries.groupBy(QUARTER, Calendar.SATURDAY);
assertThat(byQuarter.size(), equalTo(9)); // from 2013-Q1 to 2015-Q1
assertThat(byQuarter.get(0), equalTo(new Entry(timestamp(2015, JANUARY, 1), 0)));
assertThat(byQuarter.get(4), equalTo(new Entry(timestamp(2014, JANUARY, 1), 4964)));
assertThat(byQuarter.get(8), equalTo(new Entry(timestamp(2013, JANUARY, 1), 4975)));
List<Entry> byYear = entries.groupBy(YEAR, Calendar.SATURDAY);
assertThat(byYear.size(), equalTo(3)); // from 2013 to 2015
assertThat(byYear.get(0), equalTo(new Entry(timestamp(2015, JANUARY, 1), 0)));
assertThat(byYear.get(1), equalTo(new Entry(timestamp(2014, JANUARY, 1), 8227)));
assertThat(byYear.get(2), equalTo(new Entry(timestamp(2013, JANUARY, 1), 16172)));
}
@Test
public void testGetTodayValue() throws Exception
{
Habit habit = fixtures.createLongNumericalHabit(timestamp(2014, JUNE, 1));
EntryList checkmarks = habit.getComputedEntries();
DateUtils.setFixedLocalTime(unixTime(2050, MAY, 1));
assertThat(checkmarks.getTodayValue(), equalTo(0));
assertThat(checkmarks.getThisWeekValue(SATURDAY), equalTo(0));
assertThat(checkmarks.getThisMonthValue(), equalTo(0));
assertThat(checkmarks.getThisQuarterValue(), equalTo(0));
assertThat(checkmarks.getThisYearValue(), equalTo(0));
DateUtils.setFixedLocalTime(unixTime(2014, JUNE, 6));
assertThat(checkmarks.getTodayValue(), equalTo(0));
assertThat(checkmarks.getThisWeekValue(SATURDAY), equalTo(230));
assertThat(checkmarks.getThisWeekValue(SUNDAY), equalTo(230));
assertThat(checkmarks.getThisWeekValue(MONDAY), equalTo(0));
assertThat(checkmarks.getThisMonthValue(), equalTo(230));
assertThat(checkmarks.getThisQuarterValue(), equalTo(3263));
assertThat(checkmarks.getThisYearValue(), equalTo(8227));
DateUtils.setFixedLocalTime(unixTime(2014, JUNE, 1));
assertThat(checkmarks.getTodayValue(), equalTo(230));
assertThat(checkmarks.getThisWeekValue(SATURDAY), equalTo(230));
assertThat(checkmarks.getThisWeekValue(SUNDAY), equalTo(230));
assertThat(checkmarks.getThisMonthValue(), equalTo(230));
DateUtils.setFixedLocalTime(unixTime(2014, MAY, 16));
assertThat(checkmarks.getTodayValue(), equalTo(0));
assertThat(checkmarks.getThisWeekValue(SATURDAY), equalTo(419));
assertThat(checkmarks.getThisWeekValue(THURSDAY), equalTo(134));
assertThat(checkmarks.getThisMonthValue(), equalTo(1006));
DateUtils.setFixedLocalTime(unixTime(2000, MAY, 1));
assertThat(checkmarks.getTodayValue(), equalTo(UNKNOWN));
assertThat(checkmarks.getThisWeekValue(SATURDAY), equalTo(0));
assertThat(checkmarks.getThisMonthValue(), equalTo(0));
}
}

@ -40,19 +40,6 @@ public class HabitTest extends BaseUnitTest
super.setUp();
}
@Test
public void testConstructor_default()
{
Habit habit = modelFactory.buildHabit();
assertFalse(habit.isArchived());
assertThat(habit.hasReminder(), is(false));
assertNotNull(habit.getStreaks());
assertNotNull(habit.getScores());
assertNotNull(habit.getOriginalEntries());
assertNotNull(habit.getComputedEntries());
}
@Test
public void test_copyAttributes()
{
@ -86,7 +73,7 @@ public class HabitTest extends BaseUnitTest
{
Habit h = modelFactory.buildHabit();
assertFalse(h.isCompletedToday());
h.getOriginalEntries().setValue(getToday(), Entry.YES_MANUAL);
h.getOriginalEntries().add(new Entry(getToday(), Entry.YES_MANUAL));
assertTrue(h.isCompletedToday());
}
@ -99,19 +86,29 @@ public class HabitTest extends BaseUnitTest
h.setTargetValue(100.0);
assertFalse(h.isCompletedToday());
h.getOriginalEntries().setValue(getToday(), 200_000);
h.getOriginalEntries().add(new Entry(getToday(), 200_000));
h.invalidateNewerThan(Timestamp.ZERO);
assertTrue(h.isCompletedToday());
h.getOriginalEntries().setValue(getToday(), 100_000);
h.getOriginalEntries().add(new Entry(getToday(), 100_000));
h.invalidateNewerThan(Timestamp.ZERO);
assertTrue(h.isCompletedToday());
h.getOriginalEntries().setValue(getToday(), 50_000);
h.getOriginalEntries().add(new Entry(getToday(), 50_000));
h.invalidateNewerThan(Timestamp.ZERO);
assertFalse(h.isCompletedToday());
h.setTargetType(Habit.AT_MOST);
h.getOriginalEntries().setValue(getToday(), 200_000);
h.getOriginalEntries().add(new Entry(getToday(), 200_000));
h.invalidateNewerThan(Timestamp.ZERO);
assertFalse(h.isCompletedToday());
h.getOriginalEntries().setValue(getToday(), 100_000);
h.getOriginalEntries().add(new Entry(getToday(), 100_000));
h.invalidateNewerThan(Timestamp.ZERO);
assertTrue(h.isCompletedToday());
h.getOriginalEntries().setValue(getToday(), 50_000);
h.getOriginalEntries().add(new Entry(getToday(), 50_000));
h.invalidateNewerThan(Timestamp.ZERO);
assertTrue(h.isCompletedToday());
}

@ -1,150 +0,0 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.models;
import androidx.annotation.*;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import java.util.*;
import static java.util.Calendar.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
import static org.isoron.uhabits.core.models.Entry.*;
public class RepetitionListTest extends BaseUnitTest
{
@NonNull
private RepetitionList reps;
@NonNull
private Habit habit;
private Timestamp today;
private long day;
@Override
@Before
public void setUp() throws Exception
{
super.setUp();
habit = fixtures.createEmptyHabit();
reps = habit.getOriginalEntries();
today = DateUtils.getToday();
reps.setValue(today.minus(3), YES_MANUAL);
reps.setValue(today.minus(2), YES_MANUAL);
reps.setValue(today, YES_MANUAL);
reps.setValue(today.minus(7), YES_MANUAL);
reps.setValue(today.minus(5), YES_MANUAL);
}
@Override
@After
public void tearDown() throws Exception
{
super.tearDown();
}
@Test
public void test_getOldest()
{
Entry check = reps.getOldest();
assertThat(check.getTimestamp(), equalTo(today.minus(7)));
}
@Test
public void test_getWeekDayFrequency()
{
habit = fixtures.createEmptyHabit();
reps = habit.getOriginalEntries();
Random random = new Random(123L);
Integer weekdayCount[][] = new Integer[12][7];
Integer monthCount[] = new Integer[12];
Arrays.fill(monthCount, 0);
for (Integer row[] : weekdayCount) Arrays.fill(row, 0);
GregorianCalendar day = DateUtils.getStartOfTodayCalendar();
// Sets the current date to the end of November
day.set(2015, NOVEMBER, 30, 12, 0, 0);
DateUtils.setFixedLocalTime(day.getTimeInMillis());
// Add repetitions randomly from January to December
day.set(2015, JANUARY, 1, 0, 0, 0);
for (int i = 0; i < 365; i++)
{
if (random.nextBoolean())
{
int month = day.get(Calendar.MONTH);
int week = day.get(Calendar.DAY_OF_WEEK) % 7;
// Leave the month of March empty, to check that it returns null
if (month == MARCH) continue;
reps.setValue(new Timestamp(day), YES_MANUAL);
// Repetitions in December should not be counted
if (month == DECEMBER) continue;
weekdayCount[month][week]++;
monthCount[month]++;
}
day.add(Calendar.DAY_OF_YEAR, 1);
}
HashMap<Timestamp, Integer[]> freq = reps.getWeekdayFrequency();
// Repetitions until November should be counted correctly
for (int month = 0; month < 11; month++)
{
day.set(2015, month, 1, 0, 0, 0);
Integer actualCount[] = freq.get(new Timestamp(day));
if (monthCount[month] == 0) assertThat(actualCount, equalTo(null));
else assertThat(actualCount, equalTo(weekdayCount[month]));
}
// Repetitions in December should be discarded
day.set(2015, DECEMBER, 1, 0, 0, 0);
assertThat(freq.get(new Timestamp(day)), equalTo(null));
}
@Test
public void test_setValue()
{
assertThat(reps.getValue(today), equalTo(YES_MANUAL));
reps.setValue(today, NO);
assertThat(reps.getValue(today), equalTo(NO));
habit.setType(Habit.NUMBER_HABIT);
reps.setValue(today, 100);
assertThat(reps.getValue(today), equalTo(100));
reps.setValue(today, 500);
assertThat(reps.getValue(today), equalTo(500));
}
}

@ -327,34 +327,35 @@ public class ScoreListTest extends BaseUnitTest
private void check(final int offset)
{
RepetitionList entries = habit.getOriginalEntries();
Entries entries = habit.getOriginalEntries();
Timestamp today = DateUtils.getToday();
entries.setValue(today.minus(offset), YES_MANUAL);
entries.add(new Entry(today.minus(offset), YES_MANUAL));
}
private void check(final int from, final int to)
{
RepetitionList entries = habit.getOriginalEntries();
Entries entries = habit.getOriginalEntries();
Timestamp today = DateUtils.getToday();
for (int i = from; i < to; i++)
entries.setValue(today.minus(i), YES_MANUAL);
entries.add(new Entry(today.minus(i), YES_MANUAL));
habit.invalidateNewerThan(Timestamp.ZERO);
}
private void check(ArrayList<Integer> values)
{
RepetitionList entries = habit.getOriginalEntries();
Entries entries = habit.getOriginalEntries();
Timestamp today = DateUtils.getToday();
for (int i = 0; i < values.size(); i++)
if (values.get(i) == YES_MANUAL)
entries.setValue(today.minus(i), YES_MANUAL);
entries.add(new Entry(today.minus(i), YES_MANUAL));
}
private void addSkip(final int day)
{
RepetitionList entries = habit.getOriginalEntries();
Entries entries = habit.getOriginalEntries();
Timestamp today = DateUtils.getToday();
entries.setValue(today.minus(day), Entry.SKIP);
entries.add(new Entry(today.minus(day), Entry.SKIP));
}
private void checkScoreValues(double[] expectedValues)

@ -1,143 +0,0 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.models.sqlite;
import androidx.annotation.*;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.database.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.isoron.uhabits.core.test.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import java.util.*;
import static junit.framework.TestCase.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
import static org.isoron.uhabits.core.models.Entry.*;
public class SQLiteRepetitionListTest extends BaseUnitTest
{
private Habit habit;
private Timestamp today;
private RepetitionList originalCheckmarks;
private long day;
private Repository<EntryRecord> repository;
@Override
public void setUp() throws Exception
{
super.setUp();
Database db = buildMemoryDatabase();
modelFactory = new SQLModelFactory(db);
habitList = modelFactory.buildHabitList();
fixtures = new HabitFixtures(modelFactory, habitList);
repository = new Repository<>(EntryRecord.class, db);
habit = fixtures.createLongHabit();
originalCheckmarks = habit.getOriginalEntries();
today = DateUtils.getToday();
}
@Test
public void testAdd()
{
EntryRecord record = getByTimestamp(today.plus(1));
assertNull(record);
Entry rep = new Entry(today.plus(1), YES_MANUAL);
habit.getOriginalEntries().add(rep);
record = getByTimestamp(today.plus(1));
assertNotNull(record);
assertThat(record.value, equalTo(YES_MANUAL));
}
@Test
public void testGetByInterval()
{
List<Entry> checks =
originalCheckmarks.getByInterval(today.minus(10), today);
assertThat(checks.size(), equalTo(8));
assertThat(checks.get(0).getTimestamp(), equalTo(today.minus(10)));
assertThat(checks.get(4).getTimestamp(), equalTo(today.minus(5)));
assertThat(checks.get(5).getTimestamp(), equalTo(today.minus(3)));
}
@Test
public void testGetByTimestamp()
{
Entry rep = originalCheckmarks.getByTimestamp(today);
assertNotNull(rep);
assertThat(rep.getTimestamp(), equalTo(today));
rep = originalCheckmarks.getByTimestamp(today.minus(2));
assertNull(rep);
}
@Test
public void testGetOldest()
{
Entry rep = originalCheckmarks.getOldest();
assertNotNull(rep);
assertThat(rep.getTimestamp(), equalTo(today.minus(120)));
}
@Test
public void testGetOldest_withEmptyHabit()
{
Habit empty = fixtures.createEmptyHabit();
Entry rep = empty.getOriginalEntries().getOldest();
assertNull(rep);
}
@Test
public void testRemove()
{
EntryRecord record = getByTimestamp(today);
assertNotNull(record);
Entry rep = record.toEntry();
originalCheckmarks.remove(rep);
record = getByTimestamp(today);
assertNull(record);
}
@Nullable
private EntryRecord getByTimestamp(Timestamp timestamp)
{
String query = "where habit = ? and timestamp = ?";
String params[] = {
Long.toString(habit.getId()), Long.toString(timestamp.getUnixTime())
};
return repository.findFirst(query, params);
}
}

@ -91,7 +91,7 @@ public class WidgetBehaviorTest extends BaseUnitTest
if(skipEnabled) nextValue = Entry.Companion.nextToggleValueWithSkip(currentValue);
else nextValue = Entry.Companion.nextToggleValueWithoutSkip(currentValue);
habit.getOriginalEntries().setValue(timestamp, currentValue);
habit.getOriginalEntries().add(new Entry(timestamp, currentValue));
behavior.onToggleRepetition(habit, timestamp);
verify(preferences).isSkipEnabled();
verify(commandRunner).execute(
@ -106,7 +106,7 @@ public class WidgetBehaviorTest extends BaseUnitTest
public void testOnIncrement()
{
habit = fixtures.createNumericalHabit();
habit.getOriginalEntries().setValue(timestamp, 500);
habit.getOriginalEntries().add(new Entry(timestamp, 500));
behavior.onIncrement(habit, timestamp, 100);
verify(commandRunner).execute(
@ -120,7 +120,7 @@ public class WidgetBehaviorTest extends BaseUnitTest
public void testOnDecrement()
{
habit = fixtures.createNumericalHabit();
habit.getOriginalEntries().setValue(timestamp, 500);
habit.getOriginalEntries().add(new Entry(timestamp, 500));
behavior.onDecrement(habit, timestamp, 100);
verify(commandRunner).execute(

Loading…
Cancel
Save