mirror of https://github.com/iSoron/uhabits.git
parent
c904e22c0f
commit
3f74c77755
@ -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();
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue