Convert core.models.sqlite

pull/716/head
Quentin Hibon 5 years ago
parent 1cec5a6067
commit a7df0bde3e

@ -100,9 +100,9 @@ class LoopDBImporter
habit = habitList.getByUUID(habitRecord.uuid)
for (r in entryRecords) {
val t = Timestamp(r.timestamp)
val t = Timestamp(r.timestamp!!)
val (_, value) = habit!!.originalEntries.get(t)
if (value != r.value) CreateRepetitionCommand(habitList, habit, t, r.value).run()
if (value != r.value) CreateRepetitionCommand(habitList, habit, t, r.value!!).run()
}
runner.notifyListeners(command)

@ -1,296 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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.database.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import javax.inject.*;
/**
* Implementation of a {@link HabitList} that is backed by SQLite.
*/
public class SQLiteHabitList extends HabitList
{
@NonNull
private final Repository<HabitRecord> repository;
@NonNull
private final ModelFactory modelFactory;
@NonNull
private final MemoryHabitList list;
private boolean loaded = false;
@Inject
public SQLiteHabitList(@NonNull ModelFactory modelFactory)
{
super();
this.modelFactory = modelFactory;
this.list = new MemoryHabitList();
this.repository = modelFactory.buildHabitListRepository();
}
private void loadRecords()
{
if (loaded) return;
loaded = true;
list.removeAll();
List<HabitRecord> records = repository.findAll("order by position");
int expectedPosition = 0;
boolean shouldRebuildOrder = false;
for (HabitRecord rec : records)
{
if (rec.position != expectedPosition) shouldRebuildOrder = true;
expectedPosition++;
Habit h = modelFactory.buildHabit();
rec.copyTo(h);
((SQLiteEntryList) h.getOriginalEntries()).setHabitId(h.getId());
list.add(h);
}
if(shouldRebuildOrder) rebuildOrder();
}
@Override
public synchronized void add(@NonNull Habit habit)
{
loadRecords();
habit.setPosition(size());
HabitRecord record = new HabitRecord();
record.copyFrom(habit);
repository.save(record);
habit.setId(record.id);
((SQLiteEntryList) habit.getOriginalEntries()).setHabitId(record.id);
list.add(habit);
getObservable().notifyListeners();
}
@Override
@Nullable
public synchronized Habit getById(long id)
{
loadRecords();
return list.getById(id);
}
@Override
@Nullable
public synchronized Habit getByUUID(String uuid)
{
loadRecords();
return list.getByUUID(uuid);
}
@Override
@NonNull
public synchronized Habit getByPosition(int position)
{
loadRecords();
return list.getByPosition(position);
}
@NonNull
@Override
public synchronized HabitList getFiltered(HabitMatcher filter)
{
loadRecords();
return list.getFiltered(filter);
}
@Override
@NonNull
public Order getPrimaryOrder()
{
return list.getPrimaryOrder();
}
@Override
public Order getSecondaryOrder()
{
return list.getSecondaryOrder();
}
@Override
public synchronized void setPrimaryOrder(@NonNull Order order)
{
list.setPrimaryOrder(order);
getObservable().notifyListeners();
}
@Override
public synchronized void setSecondaryOrder(@NonNull Order order)
{
list.setSecondaryOrder(order);
getObservable().notifyListeners();
}
@Override
public synchronized int indexOf(@NonNull Habit h)
{
loadRecords();
return list.indexOf(h);
}
@NotNull
@Override
public synchronized Iterator<Habit> iterator()
{
loadRecords();
return list.iterator();
}
private synchronized void rebuildOrder()
{
List<HabitRecord> records = repository.findAll("order by position");
repository.executeAsTransaction(() ->
{
int pos = 0;
for (HabitRecord r : records)
{
if (r.position != pos)
{
r.position = pos;
repository.save(r);
}
pos++;
}
});
}
@Override
public synchronized void remove(@NonNull Habit habit)
{
loadRecords();
reorder(habit, list.getByPosition(size() - 1));
list.remove(habit);
HabitRecord record = repository.find(habit.getId());
if (record == null) throw new RuntimeException("habit not in database");
repository.executeAsTransaction(() ->
{
habit.getOriginalEntries().clear();
repository.remove(record);
});
getObservable().notifyListeners();
}
@Override
public synchronized void removeAll()
{
list.removeAll();
repository.execSQL("delete from habits");
repository.execSQL("delete from repetitions");
getObservable().notifyListeners();
}
@Override
public synchronized void reorder(@NonNull Habit from, @NonNull Habit to)
{
loadRecords();
list.reorder(from, to);
HabitRecord fromRecord = repository.find(from.getId());
HabitRecord toRecord = repository.find(to.getId());
if (fromRecord == null)
throw new RuntimeException("habit not in database");
if (toRecord == null)
throw new RuntimeException("habit not in database");
if (toRecord.position < fromRecord.position)
{
repository.execSQL("update habits set position = position + 1 " +
"where position >= ? and position < ?",
toRecord.position, fromRecord.position);
}
else
{
repository.execSQL("update habits set position = position - 1 " +
"where position > ? and position <= ?",
fromRecord.position, toRecord.position);
}
fromRecord.position = toRecord.position;
repository.save(fromRecord);
getObservable().notifyListeners();
}
@Override
public synchronized void repair()
{
loadRecords();
rebuildOrder();
getObservable().notifyListeners();
}
@Override
public synchronized int size()
{
loadRecords();
return list.size();
}
@Override
public synchronized void update(List<Habit> habits)
{
loadRecords();
list.update(habits);
for (Habit h : habits)
{
HabitRecord record = repository.find(h.getId());
if (record == null) continue;
record.copyFrom(h);
repository.save(record);
}
getObservable().notifyListeners();
}
@Override
public void resort()
{
list.resort();
getObservable().notifyListeners();
}
public synchronized void reload()
{
loaded = false;
}
}

@ -0,0 +1,226 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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.Repository
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.HabitMatcher
import org.isoron.uhabits.core.models.ModelFactory
import org.isoron.uhabits.core.models.memory.MemoryHabitList
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
import javax.inject.Inject
/**
* Implementation of a [HabitList] that is backed by SQLite.
*/
class SQLiteHabitList @Inject constructor(private val modelFactory: ModelFactory) : HabitList() {
private val repository: Repository<HabitRecord>
private val list: MemoryHabitList = MemoryHabitList()
private var loaded = false
private fun loadRecords() {
if (loaded) return
loaded = true
list.removeAll()
val records = repository.findAll("order by position")
var shouldRebuildOrder = false
for ((expectedPosition, rec) in records.withIndex()) {
if (rec.position != expectedPosition) shouldRebuildOrder = true
val h = modelFactory.buildHabit()
rec.copyTo(h)
(h.originalEntries as SQLiteEntryList).habitId = h.id
list.add(h)
}
if (shouldRebuildOrder) rebuildOrder()
}
@Synchronized
override fun add(habit: Habit) {
loadRecords()
habit.position = size()
val record = HabitRecord()
record.copyFrom(habit)
repository.save(record)
habit.id = record.id
(habit.originalEntries as SQLiteEntryList).habitId = record.id
list.add(habit)
observable.notifyListeners()
}
@Synchronized
override fun getById(id: Long): Habit? {
loadRecords()
return list.getById(id)
}
@Synchronized
override fun getByUUID(uuid: String?): Habit? {
loadRecords()
return list.getByUUID(uuid)
}
@Synchronized
override fun getByPosition(position: Int): Habit {
loadRecords()
return list.getByPosition(position)
}
@Synchronized
override fun getFiltered(matcher: HabitMatcher?): HabitList {
loadRecords()
return list.getFiltered(matcher)
}
@set:Synchronized
override var primaryOrder: Order
get() = list.primaryOrder
set(order) {
list.primaryOrder = order
observable.notifyListeners()
}
@set:Synchronized
override var secondaryOrder: Order
get() = list.secondaryOrder
set(order) {
list.secondaryOrder = order
observable.notifyListeners()
}
@Synchronized
override fun indexOf(h: Habit): Int {
loadRecords()
return list.indexOf(h)
}
@Synchronized
override fun iterator(): Iterator<Habit> {
loadRecords()
return list.iterator()
}
@Synchronized
private fun rebuildOrder() {
val records = repository.findAll("order by position")
repository.executeAsTransaction {
for ((pos, r) in records.withIndex()) {
if (r.position != pos) {
r.position = pos
repository.save(r)
}
}
}
}
@Synchronized
override fun remove(h: Habit) {
loadRecords()
reorder(h, list.getByPosition(size() - 1))
list.remove(h)
val record = repository.find(
h.id!!
) ?: throw RuntimeException("habit not in database")
repository.executeAsTransaction {
h.originalEntries.clear()
repository.remove(record)
}
observable.notifyListeners()
}
@Synchronized
override fun removeAll() {
list.removeAll()
repository.execSQL("delete from habits")
repository.execSQL("delete from repetitions")
observable.notifyListeners()
}
@Synchronized
override fun reorder(from: Habit, to: Habit) {
loadRecords()
list.reorder(from, to)
val fromRecord = repository.find(
from.id!!
)
val toRecord = repository.find(
to.id!!
)
if (fromRecord == null) throw RuntimeException("habit not in database")
if (toRecord == null) throw RuntimeException("habit not in database")
if (toRecord.position!! < fromRecord.position!!) {
repository.execSQL(
"update habits set position = position + 1 " +
"where position >= ? and position < ?",
toRecord.position!!,
fromRecord.position!!
)
} else {
repository.execSQL(
"update habits set position = position - 1 " +
"where position > ? and position <= ?",
fromRecord.position!!,
toRecord.position!!
)
}
fromRecord.position = toRecord.position
repository.save(fromRecord)
observable.notifyListeners()
}
@Synchronized
override fun repair() {
loadRecords()
rebuildOrder()
observable.notifyListeners()
}
@Synchronized
override fun size(): Int {
loadRecords()
return list.size()
}
@Synchronized
override fun update(habits: List<Habit?>?) {
loadRecords()
list.update(habits)
for (h in habits!!) {
val record = repository.find(
h!!.id!!
) ?: continue
record.copyFrom(h)
repository.save(record)
}
observable.notifyListeners()
}
override fun resort() {
list.resort()
observable.notifyListeners()
}
@Synchronized
fun reload() {
loaded = false
}
init {
repository = modelFactory.buildHabitListRepository()
}
}

@ -16,40 +16,42 @@
* 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.records
package org.isoron.uhabits.core.models.sqlite.records;
import org.isoron.uhabits.core.database.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.database.Column
import org.isoron.uhabits.core.database.Table
import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.core.models.Timestamp
/**
* The SQLite database record corresponding to a {@link Entry}.
* The SQLite database record corresponding to a [Entry].
*/
@Table(name = "Repetitions")
public class EntryRecord
{
public HabitRecord habit;
@Column(name = "habit")
public Long habitId;
class EntryRecord {
var habit: HabitRecord? = null
@Column
public Long timestamp;
@field:Column(name = "habit")
var habitId: Long? = null
@Column
public Integer value;
@field:Column
var timestamp: Long? = null
@Column
public Long id;
@field:Column
var value: Int? = null
public void copyFrom(Entry entry)
{
timestamp = entry.getTimestamp().getUnixTime();
value = entry.getValue();
@field:Column
var id: Long? = null
fun copyFrom(entry: Entry) {
timestamp = entry.timestamp.unixTime
value = entry.value
}
public Entry toEntry()
{
return new Entry(new Timestamp(timestamp), value);
fun toEntry(): Entry {
return Entry(
Timestamp(
timestamp!!
),
value!!
)
}
}

@ -1,141 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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.records;
import org.isoron.uhabits.core.database.*;
import org.isoron.uhabits.core.models.*;
import java.util.Objects;
/**
* The SQLite database record corresponding to a {@link Habit}.
*/
@Table(name = "habits")
public class HabitRecord
{
@Column
public String description;
@Column
public String question;
@Column
public String name;
@Column(name = "freq_num")
public Integer freqNum;
@Column(name = "freq_den")
public Integer freqDen;
@Column
public Integer color;
@Column
public Integer position;
@Column(name = "reminder_hour")
public Integer reminderHour;
@Column(name = "reminder_min")
public Integer reminderMin;
@Column(name = "reminder_days")
public Integer reminderDays;
@Column
public Integer highlight;
@Column
public Integer archived;
@Column
public Integer type;
@Column(name = "target_value")
public Double targetValue;
@Column(name = "target_type")
public Integer targetType;
@Column
public String unit;
@Column
public Long id;
@Column
public String uuid;
public void copyFrom(Habit model)
{
this.id = model.getId();
this.name = model.getName();
this.description = model.getDescription();
this.highlight = 0;
this.color = model.getColor().getPaletteIndex();
this.archived = model.isArchived() ? 1 : 0;
this.type = model.getType();
this.targetType = model.getTargetType();
this.targetValue = model.getTargetValue();
this.unit = model.getUnit();
this.position = model.getPosition();
this.question = model.getQuestion();
this.uuid = model.getUuid();
Frequency freq = model.getFrequency();
this.freqNum = freq.getNumerator();
this.freqDen = freq.getDenominator();
this.reminderDays = 0;
this.reminderMin = null;
this.reminderHour = null;
if (model.hasReminder())
{
Reminder reminder = model.getReminder();
this.reminderHour = Objects.requireNonNull(reminder).getHour();
this.reminderMin = reminder.getMinute();
this.reminderDays = reminder.getDays().toInteger();
}
}
public void copyTo(Habit habit)
{
habit.setId(this.id);
habit.setName(this.name);
habit.setDescription(this.description);
habit.setQuestion(this.question);
habit.setFrequency(new Frequency(this.freqNum, this.freqDen));
habit.setColor(new PaletteColor(this.color));
habit.setArchived(this.archived != 0);
habit.setType(this.type);
habit.setTargetType(this.targetType);
habit.setTargetValue(this.targetValue);
habit.setUnit(this.unit);
habit.setPosition(this.position);
habit.setUuid(this.uuid);
if (reminderHour != null && reminderMin != null)
{
habit.setReminder(new Reminder(reminderHour, reminderMin,
new WeekdayList(reminderDays)));
}
}
}

@ -0,0 +1,138 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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.records
import org.isoron.uhabits.core.database.Column
import org.isoron.uhabits.core.database.Table
import org.isoron.uhabits.core.models.Frequency
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.models.Reminder
import org.isoron.uhabits.core.models.WeekdayList
import java.util.Objects
/**
* The SQLite database record corresponding to a [Habit].
*/
@Table(name = "habits")
class HabitRecord {
@field:Column
var description: String? = null
@field:Column
var question: String? = null
@field:Column
var name: String? = null
@field:Column(name = "freq_num")
var freqNum: Int? = null
@field:Column(name = "freq_den")
var freqDen: Int? = null
@field:Column
var color: Int? = null
@field:Column
var position: Int? = null
@field:Column(name = "reminder_hour")
var reminderHour: Int? = null
@field:Column(name = "reminder_min")
var reminderMin: Int? = null
@field:Column(name = "reminder_days")
var reminderDays: Int? = null
@field:Column
var highlight: Int? = null
@field:Column
var archived: Int? = null
@field:Column
var type: Int? = null
@field:Column(name = "target_value")
var targetValue: Double? = null
@field:Column(name = "target_type")
var targetType: Int? = null
@field:Column
var unit: String? = null
@field:Column
var id: Long? = null
@field:Column
var uuid: String? = null
fun copyFrom(model: Habit?) {
id = model!!.id
name = model.name
description = model.description
highlight = 0
color = model.color.paletteIndex
archived = if (model.isArchived) 1 else 0
type = model.type
targetType = model.targetType
targetValue = model.targetValue
unit = model.unit
position = model.position
question = model.question
uuid = model.uuid
val (numerator, denominator) = model.frequency
freqNum = numerator
freqDen = denominator
reminderDays = 0
reminderMin = null
reminderHour = null
if (model.hasReminder()) {
val reminder = model.reminder
reminderHour = Objects.requireNonNull(reminder)!!.hour
reminderMin = reminder!!.minute
reminderDays = reminder.days.toInteger()
}
}
fun copyTo(habit: Habit) {
habit.id = id
habit.name = name!!
habit.description = description!!
habit.question = question!!
habit.frequency = Frequency(freqNum!!, freqDen!!)
habit.color = PaletteColor(color!!)
habit.isArchived = archived != 0
habit.type = type!!
habit.targetType = targetType!!
habit.targetValue = targetValue!!
habit.unit = unit!!
habit.position = position!!
habit.uuid = uuid
if (reminderHour != null && reminderMin != null) {
habit.reminder = Reminder(
reminderHour!!,
reminderMin!!,
WeekdayList(reminderDays!!)
)
}
}
}
Loading…
Cancel
Save