Implement SQLiteEntries; rename RepetitionRecord to EntryRecord

pull/699/head
Alinson S. Xavier 5 years ago
parent 27023e50ae
commit c904e22c0f

@ -97,15 +97,15 @@ public class LoopDBImporter extends AbstractImporter
helper.migrateTo(DATABASE_VERSION);
Repository<HabitRecord> habitsRepository;
Repository<RepetitionRecord> repsRepository;
Repository<EntryRecord> entryRepository;
habitsRepository = new Repository<>(HabitRecord.class, db);
repsRepository = new Repository<>(RepetitionRecord.class, db);
entryRepository = new Repository<>(EntryRecord.class, db);
List<HabitRecord> records = habitsRepository.findAll("order by position");
for (HabitRecord habitRecord : records)
{
List<RepetitionRecord> reps =
repsRepository.findAll("where habit = ?",
List<EntryRecord> reps =
entryRepository.findAll("where habit = ?",
habitRecord.id.toString());
Habit habit = habitList.getByUUID(habitRecord.uuid);
@ -127,7 +127,7 @@ public class LoopDBImporter extends AbstractImporter
// Reload saved version of the habit
habit = habitList.getByUUID(habitRecord.uuid);
for (RepetitionRecord r : reps)
for (EntryRecord r : reps)
{
Timestamp t = new Timestamp(r.timestamp);
Entry entry = habit.getOriginalEntries().getByTimestamp(t);

@ -26,7 +26,7 @@ import org.isoron.uhabits.core.utils.*
import kotlin.collections.set
import kotlin.math.*
class Entries {
open class Entries {
private val entriesByTimestamp: HashMap<Timestamp, Entry> = HashMap()
@ -34,7 +34,7 @@ class Entries {
* Returns the entry corresponding to the given timestamp. If no entry with such timestamp
* has been previously added, returns Entry(timestamp, UNKNOWN).
*/
fun get(timestamp: Timestamp): Entry {
open fun get(timestamp: Timestamp): Entry {
return entriesByTimestamp[timestamp] ?: Entry(timestamp, UNKNOWN)
}
@ -43,7 +43,7 @@ class Entries {
* newest entry, and the last element corresponds to the oldest. The interval endpoints are
* included.
*/
fun getByInterval(from: Timestamp, to: Timestamp): List<Entry> {
open fun getByInterval(from: Timestamp, to: Timestamp): List<Entry> {
val result = mutableListOf<Entry>()
var current = to
while (current >= from) {
@ -57,7 +57,7 @@ class Entries {
* Adds the given entry to the list. If another entry with the same timestamp already exists,
* replaces it.
*/
fun add(entry: Entry) {
open fun add(entry: Entry) {
entriesByTimestamp[entry.timestamp] = entry
}
@ -65,7 +65,7 @@ class Entries {
* Returns all entries whose values are known, sorted by timestamp. The first element
* corresponds to the newest entry, and the last element corresponds to the oldest.
*/
fun getKnown(): List<Entry> {
open fun getKnown(): List<Entry> {
return entriesByTimestamp.values.sortedBy { it.timestamp }.reversed()
}
@ -78,7 +78,7 @@ class Entries {
* entries. For numerical habits, the value is the total sum. The field [firstWeekday] is only
* relevant when grouping by week.
*/
fun groupBy(
open fun groupBy(
field: DateUtils.TruncateField,
firstWeekday: Int,
isNumerical: Boolean,
@ -111,7 +111,7 @@ class Entries {
* For boolean habits, this function creates additional entries (with value YES_AUTO) according
* to the frequency of the habit. For numerical habits, this function simply copies all entries.
*/
fun computeFrom(
open fun computeFrom(
other: Entries,
frequency: Frequency,
isNumerical: Boolean,
@ -129,18 +129,18 @@ class Entries {
}
}
data class Interval(val begin: Timestamp, val center: Timestamp, val end: Timestamp) {
val length: Int
get() = begin.daysUntil(end) + 1;
}
/**
* Removes all known entries.
*/
fun clear() {
open fun clear() {
entriesByTimestamp.clear()
}
data class Interval(val begin: Timestamp, val center: Timestamp, val end: Timestamp) {
val length: Int
get() = begin.daysUntil(end) + 1;
}
/**
* Converts a list of intervals into a list of entries. Entries that fall outside of any
* interval receive value UNKNOWN. Entries that fall within an interval but do not appear

@ -51,5 +51,5 @@ interface ModelFactory {
fun buildScoreList(): ScoreList
fun buildStreakList(): StreakList
fun buildHabitListRepository(): Repository<HabitRecord>
fun buildRepetitionListRepository(): Repository<RepetitionRecord>
fun buildRepetitionListRepository(): Repository<EntryRecord>
}

@ -44,5 +44,5 @@ class SQLModelFactory
Repository(HabitRecord::class.java, database)
override fun buildRepetitionListRepository() =
Repository(RepetitionRecord::class.java, database)
Repository(EntryRecord::class.java, database)
}

@ -0,0 +1,86 @@
/*
* Copyright (C) 2016-2020 Á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 org.isoron.uhabits.core.database.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.models.sqlite.records.*
import org.isoron.uhabits.core.utils.*
class SQLiteEntries(database: Database) : Entries() {
val repository = Repository(EntryRecord::class.java, database)
var habitId: Long? = null
var isLoaded = false
private fun loadRecords() {
if (isLoaded) return
val habitId = habitId ?: throw IllegalStateException("habitId must be set")
val records = repository.findAll("where habit = ? order by timestamp",
habitId.toString())
for (rec in records) super.add(rec.toEntry())
isLoaded = true
}
override fun get(timestamp: Timestamp): Entry {
loadRecords()
return super.get(timestamp)
}
override fun getByInterval(from: Timestamp, to: Timestamp): List<Entry> {
loadRecords()
return super.getByInterval(from, to)
}
override fun add(entry: Entry) {
loadRecords()
val habitId = habitId ?: throw IllegalStateException("habitId must be set")
// Remove existing rows
repository.execSQL("delete from repetitions where habit = ? and timestamp = ?",
habitId.toString(),
entry.timestamp.unixTime.toString())
// Add new row
val record = EntryRecord().apply { copyFrom(entry) }
record.habitId = habitId
repository.save(record)
// Add to memory list
super.add(entry)
}
override fun getKnown(): List<Entry> {
loadRecords()
return super.getKnown()
}
override fun groupBy(field: DateUtils.TruncateField, firstWeekday: Int, isNumerical: Boolean): List<Entry> {
loadRecords()
return super.groupBy(field, firstWeekday, isNumerical)
}
override fun computeFrom(other: Entries, frequency: Frequency, isNumerical: Boolean) {
throw UnsupportedOperationException()
}
override fun clear() {
throw UnsupportedOperationException()
}
}

@ -36,7 +36,7 @@ import java.util.*;
*/
public class SQLiteRepetitionList extends RepetitionList
{
private final Repository<RepetitionRecord> repository;
private final Repository<EntryRecord> repository;
private boolean loaded = false;
@ -51,12 +51,12 @@ public class SQLiteRepetitionList extends RepetitionList
loaded = true;
check(habit.getId());
List<RepetitionRecord> records =
List<EntryRecord> records =
repository.findAll("where habit = ? order by timestamp",
habit.getId().toString());
for (RepetitionRecord rec : records)
super.add(rec.toCheckmark());
for (EntryRecord rec : records)
super.add(rec.toEntry());
}
@Override
@ -65,8 +65,8 @@ public class SQLiteRepetitionList extends RepetitionList
loadRecords();
super.add(entry);
check(habit.getId());
RepetitionRecord record = new RepetitionRecord();
record.habit_id = habit.getId();
EntryRecord record = new EntryRecord();
record.habitId = habit.getId();
record.copyFrom(entry);
repository.save(record);
}

@ -28,12 +28,12 @@ import org.isoron.uhabits.core.models.*;
* The SQLite database record corresponding to a {@link Entry}.
*/
@Table(name = "Repetitions")
public class RepetitionRecord
public class EntryRecord
{
public HabitRecord habit;
@Column(name = "habit")
public Long habit_id;
public Long habitId;
@Column
public Long timestamp;
@ -50,7 +50,7 @@ public class RepetitionRecord
value = entry.getValue();
}
public Entry toCheckmark()
public Entry toEntry()
{
return new Entry(new Timestamp(timestamp), value);
}

@ -120,7 +120,7 @@ public class BaseUnitTest
}
protected Database buildMemoryDatabase()
public static Database buildMemoryDatabase()
{
try
{

@ -0,0 +1,105 @@
/*
* Copyright (C) 2016-2020 Á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 junit.framework.Assert.*
import org.isoron.uhabits.core.BaseUnitTest.*
import org.isoron.uhabits.core.database.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
import org.isoron.uhabits.core.models.sqlite.records.*
import org.isoron.uhabits.core.utils.*
import org.junit.*
class SQLiteEntriesTest {
private val database = buildMemoryDatabase()
private val repository = Repository(EntryRecord::class.java, database)
private val entries = SQLiteEntries(database)
private val today = DateUtils.getToday()
@Before
fun setUp() {
// Create a habit and add it to the database to satisfy foreign key requirements
val factory = SQLModelFactory(database)
val habitList = factory.buildHabitList()
val habit = factory.buildHabit()
habitList.add(habit)
entries.habitId = habit.id
}
@Test
fun testLoad() {
val today = DateUtils.getToday()
repository.save(EntryRecord().apply {
habitId = entries.habitId
timestamp = today.unixTime
value = 500
})
repository.save(EntryRecord().apply {
habitId = entries.habitId
timestamp = today.minus(5).unixTime
value = 300
})
assertEquals(
Entry(timestamp = today, value = 500),
entries.get(today),
)
assertEquals(
Entry(timestamp = today.minus(1), value = UNKNOWN),
entries.get(today.minus(1)),
)
assertEquals(
Entry(timestamp = today.minus(5), value = 300),
entries.get(today.minus(5)),
)
}
@Test
fun testAdd() {
assertNull(getByTimestamp(1, today))
val original = Entry(today, 150)
entries.add(original)
val retrieved = getByTimestamp(1, today)
assertNotNull(retrieved)
assertEquals(original, retrieved!!.toEntry())
val replacement = Entry(today, 90)
entries.add(replacement)
val retrieved2 = getByTimestamp(1, today)
assertNotNull(retrieved2)
assertEquals(replacement, retrieved2!!.toEntry())
}
private fun getByTimestamp(
habitId: Int,
timestamp: Timestamp,
): EntryRecord? {
return repository.findFirst(
"where habit = ? and timestamp = ?",
habitId.toString(),
timestamp.unixTime.toString(),
)
}
}

@ -46,7 +46,7 @@ public class SQLiteRepetitionListTest extends BaseUnitTest
private long day;
private Repository<RepetitionRecord> repository;
private Repository<EntryRecord> repository;
@Override
public void setUp() throws Exception
@ -57,7 +57,7 @@ public class SQLiteRepetitionListTest extends BaseUnitTest
modelFactory = new SQLModelFactory(db);
habitList = modelFactory.buildHabitList();
fixtures = new HabitFixtures(modelFactory, habitList);
repository = new Repository<>(RepetitionRecord.class, db);
repository = new Repository<>(EntryRecord.class, db);
habit = fixtures.createLongHabit();
originalCheckmarks = habit.getOriginalEntries();
@ -67,7 +67,7 @@ public class SQLiteRepetitionListTest extends BaseUnitTest
@Test
public void testAdd()
{
RepetitionRecord record = getByTimestamp(today.plus(1));
EntryRecord record = getByTimestamp(today.plus(1));
assertNull(record);
Entry rep = new Entry(today.plus(1), YES_MANUAL);
@ -120,10 +120,10 @@ public class SQLiteRepetitionListTest extends BaseUnitTest
@Test
public void testRemove()
{
RepetitionRecord record = getByTimestamp(today);
EntryRecord record = getByTimestamp(today);
assertNotNull(record);
Entry rep = record.toCheckmark();
Entry rep = record.toEntry();
originalCheckmarks.remove(rep);
record = getByTimestamp(today);
@ -131,7 +131,7 @@ public class SQLiteRepetitionListTest extends BaseUnitTest
}
@Nullable
private RepetitionRecord getByTimestamp(Timestamp timestamp)
private EntryRecord getByTimestamp(Timestamp timestamp)
{
String query = "where habit = ? and timestamp = ?";
String params[] = {

@ -28,14 +28,14 @@ import org.junit.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
public class RepetitionRecordTest extends BaseUnitTest
public class EntryRecordTest extends BaseUnitTest
{
@Test
public void testRecord() throws Exception
{
Entry check = new Entry(Timestamp.ZERO.plus(100), 50);
RepetitionRecord record = new RepetitionRecord();
EntryRecord record = new EntryRecord();
record.copyFrom(check);
assertThat(check, equalTo(record.toCheckmark()));
assertThat(check, equalTo(record.toEntry()));
}
}
Loading…
Cancel
Save