mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Implement SQLiteEntries; rename RepetitionRecord to EntryRecord
This commit is contained in:
@@ -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()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user