Creating the Habitgroup model

pull/2020/head
Dharanish 1 year ago
parent 340bde9f69
commit 73387e5e63

@ -79,6 +79,10 @@ class HabitsApplication : Application() {
val habitList = component.habitList
for (h in habitList) h.recompute()
val habitGroupList = component.habitGroupList
for (hgr in habitGroupList) hgr.recompute()
habitGroupList.populateGroupsWith(habitList)
widgetUpdater = component.widgetUpdater.apply {
startListening()
scheduleStartDayWidgetUpdate()

@ -24,6 +24,7 @@ import org.isoron.uhabits.core.AppScope
import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.io.GenericImporter
import org.isoron.uhabits.core.io.Logging
import org.isoron.uhabits.core.models.HabitGroupList
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.ModelFactory
import org.isoron.uhabits.core.preferences.Preferences
@ -50,6 +51,7 @@ interface HabitsApplicationComponent {
val genericImporter: GenericImporter
val habitCardListCache: HabitCardListCache
val habitList: HabitList
val habitGroupList: HabitGroupList
val intentFactory: IntentFactory
val intentParser: IntentParser
val logging: Logging

@ -26,9 +26,11 @@ import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.database.Database
import org.isoron.uhabits.core.database.DatabaseOpener
import org.isoron.uhabits.core.io.Logging
import org.isoron.uhabits.core.models.HabitGroupList
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.ModelFactory
import org.isoron.uhabits.core.models.sqlite.SQLModelFactory
import org.isoron.uhabits.core.models.sqlite.SQLiteHabitGroupList
import org.isoron.uhabits.core.models.sqlite.SQLiteHabitList
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.preferences.WidgetPreferences
@ -97,6 +99,12 @@ class HabitsModule(dbFile: File) {
return list
}
@Provides
@AppScope
fun getHabitGroupList(list: SQLiteHabitGroupList): HabitGroupList {
return list
}
@Provides
@AppScope
fun getDatabaseOpener(opener: AndroidDatabaseOpener): DatabaseOpener {

@ -40,12 +40,15 @@ data class Habit(
val computedEntries: EntryList,
val originalEntries: EntryList,
val scores: ScoreList,
val streaks: StreakList
val streaks: StreakList,
var parentID: Long? = null,
var parentUUID: String? = null
) {
init {
if (uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "")
}
var parent: HabitGroup? = null
var observable = ModelObservable()
val isNumerical: Boolean
@ -111,6 +114,14 @@ data class Habit(
return computedEntries.getKnown().lastOrNull()?.timestamp ?: DateUtils.getTodayWithOffset()
}
fun hierarchyLevel(): Int {
return if (parentID == null) {
0
} else {
1 + parent!!.hierarchyLevel()
}
}
fun copyFrom(other: Habit) {
this.color = other.color
this.description = other.description
@ -127,6 +138,8 @@ data class Habit(
this.type = other.type
this.unit = other.unit
this.uuid = other.uuid
this.parentID = other.parentID
this.parentUUID = other.parentUUID
}
override fun equals(other: Any?): Boolean {
@ -148,6 +161,8 @@ data class Habit(
if (type != other.type) return false
if (unit != other.unit) return false
if (uuid != other.uuid) return false
if (parentID != other.parentID) return false
if (parentUUID != other.parentUUID) return false
return true
}
@ -168,6 +183,8 @@ data class Habit(
result = 31 * result + type.value
result = 31 * result + unit.hashCode()
result = 31 * result + (uuid?.hashCode() ?: 0)
result = 31 * result + (parentID?.hashCode() ?: 0)
result = 31 * result + (parentUUID?.hashCode() ?: 0)
return result
}
}

@ -12,17 +12,19 @@ data class HabitGroup(
var position: Int = 0,
var question: String = "",
var reminder: Reminder? = null,
var unit: String = "",
var uuid: String? = null,
var habitList: HabitList,
var habitGroupList: HabitGroupList,
val scores: ScoreList,
val streaks: StreakList
val streaks: StreakList,
var parentID: Long? = null,
var parentUUID: String? = null
) {
init {
if (uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "")
}
var parent: HabitGroup? = null
var observable = ModelObservable()
val uriString: String
@ -76,7 +78,7 @@ data class HabitGroup(
)
}
fun copyFrom(other: Habit) {
fun copyFrom(other: HabitGroup) {
this.color = other.color
this.description = other.description
// this.id should not be copied
@ -85,8 +87,9 @@ data class HabitGroup(
this.position = other.position
this.question = other.question
this.reminder = other.reminder
this.unit = other.unit
this.uuid = other.uuid
this.parentID = other.parentID
this.parentUUID = other.parentUUID
}
override fun equals(other: Any?): Boolean {
@ -101,8 +104,9 @@ data class HabitGroup(
if (position != other.position) return false
if (question != other.question) return false
if (reminder != other.reminder) return false
if (unit != other.unit) return false
if (uuid != other.uuid) return false
if (parentID != other.parentID) return false
if (parentUUID != other.parentUUID) return false
return true
}
@ -116,8 +120,37 @@ data class HabitGroup(
result = 31 * result + position
result = 31 * result + question.hashCode()
result = 31 * result + (reminder?.hashCode() ?: 0)
result = 31 * result + unit.hashCode()
result = 31 * result + (uuid?.hashCode() ?: 0)
result = 31 * result + (parentID?.hashCode() ?: 0)
result = 31 * result + (parentUUID?.hashCode() ?: 0)
return result
}
fun hierarchyLevel(): Int {
return if (parentID == null) {
0
} else {
1 + parent!!.hierarchyLevel()
}
}
fun getHabitByUUIDDeep(uuid: String?): Habit? {
val habit = habitList.getByUUID(uuid)
if (habit != null) return habit
for (hgr in habitGroupList) {
val found = hgr.getHabitByUUIDDeep(uuid)
if (found != null) return found
}
return null
}
fun getHabitGroupByUUIDDeep(uuid: String?): HabitGroup? {
val habitGroup = habitGroupList.getByUUID(uuid)
if (habitGroup != null) return habitGroup
for (hgr in habitGroupList) {
val found = hgr.getHabitGroupByUUIDDeep(uuid)
if (found != null) return found
}
return null
}
}

@ -1,6 +1,7 @@
package org.isoron.uhabits.core.models
import com.opencsv.CSVWriter
import org.isoron.uhabits.core.models.HabitList.Order
import java.io.IOException
import java.io.Writer
import java.util.LinkedList
@ -63,6 +64,30 @@ abstract class HabitGroupList : Iterable<HabitGroup> {
*/
abstract fun getByUUID(uuid: String?): HabitGroup?
/**
* Returns the habit with the specified UUID which is
* present at any hierarchy within this list.
*/
fun getHabitByUUIDDeep(uuid: String?): Habit? {
for (hgr in this) {
val habit = hgr.getHabitByUUIDDeep(uuid)
if (habit != null) {
return habit
}
}
return null
}
fun getHabitGroupByUUIDDeep(uuid: String?): HabitGroup? {
for (hgr in this) {
val habit = hgr.getHabitGroupByUUIDDeep(uuid)
if (habit != null) {
return habit
}
}
return null
}
/**
* Returns the habit that occupies a certain position.
*
@ -150,6 +175,42 @@ abstract class HabitGroupList : Iterable<HabitGroup> {
update(listOf(habitGroup))
}
fun populateGroupsWith(habitList: HabitList) {
val toRemove = mutableListOf<String?>()
for (habit in habitList) {
val hgr = getByUUID(habit.parentUUID)
if (hgr != null) {
hgr.habitList.add(habit)
habit.parent = hgr
toRemove.add(habit.uuid)
}
}
for (uuid in toRemove) {
val h = habitList.getByUUID(uuid)
if (h != null) {
habitList.remove(h)
}
}
toRemove.clear()
for (hgr1 in this) {
val hgr2 = getByUUID(hgr1.parentUUID)
if (hgr2 != null) {
hgr2.habitGroupList.add(hgr1)
toRemove.add(hgr1.uuid)
hgr1.parent = hgr2
}
}
for (uuid in toRemove) {
val h = getByUUID(uuid)
if (h != null) {
remove(h)
}
}
for (hgr in this) {
hgr.recompute()
}
}
/**
* Writes the list of habits to the given writer, in CSV format. There is
* one line for each habit, containing the fields name, description,
@ -186,15 +247,4 @@ abstract class HabitGroupList : Iterable<HabitGroup> {
}
abstract fun resort()
enum class Order {
BY_NAME_ASC,
BY_NAME_DESC,
BY_COLOR_ASC,
BY_COLOR_DESC,
BY_SCORE_ASC,
BY_SCORE_DESC,
BY_STATUS_ASC,
BY_STATUS_DESC,
BY_POSITION
}
}

@ -32,6 +32,14 @@ data class HabitMatcher(
return true
}
fun matches(habitGroup: HabitGroup): Boolean {
if (!isArchivedAllowed && habitGroup.isArchived) return false
if (isReminderRequired && !habitGroup.hasReminder()) return false
if (!isCompletedAllowed && habitGroup.isCompletedToday()) return false
if (!isEnteredAllowed && habitGroup.isEnteredToday()) return false
return true
}
companion object {
@JvmField
val WITH_ALARM = HabitMatcher(

@ -20,6 +20,7 @@ package org.isoron.uhabits.core.models
import org.isoron.uhabits.core.database.Repository
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
import org.isoron.uhabits.core.models.sqlite.records.HabitGroupRecord
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
/**
@ -38,11 +39,25 @@ interface ModelFactory {
computedEntries = buildComputedEntries()
)
}
fun buildHabitGroup(): HabitGroup {
val habits = buildHabitList()
val groups = buildHabitGroupList()
val scores = buildScoreList()
val streaks = buildStreakList()
return HabitGroup(
habitList = habits,
habitGroupList = groups,
scores = scores,
streaks = streaks
)
}
fun buildComputedEntries(): EntryList
fun buildOriginalEntries(): EntryList
fun buildHabitList(): HabitList
fun buildHabitGroupList(): HabitGroupList
fun buildScoreList(): ScoreList
fun buildStreakList(): StreakList
fun buildHabitListRepository(): Repository<HabitRecord>
fun buildRepetitionListRepository(): Repository<EntryRecord>
fun buildHabitGroupListRepository(): Repository<HabitGroupRecord>
}

@ -0,0 +1,216 @@
package org.isoron.uhabits.core.models.memory
import org.isoron.uhabits.core.models.HabitGroup
import org.isoron.uhabits.core.models.HabitGroupList
import org.isoron.uhabits.core.models.HabitList.Order
import org.isoron.uhabits.core.models.HabitMatcher
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
import java.util.LinkedList
import java.util.Objects
/**
* In-memory implementation of [HabitGroupList].
*/
class MemoryHabitGroupList : HabitGroupList {
private val list = LinkedList<HabitGroup>()
@get:Synchronized
override var primaryOrder = Order.BY_POSITION
set(value) {
field = value
comparator = getComposedComparatorByOrder(primaryOrder, secondaryOrder)
resort()
}
@get:Synchronized
override var secondaryOrder = Order.BY_NAME_ASC
set(value) {
field = value
comparator = getComposedComparatorByOrder(primaryOrder, secondaryOrder)
resort()
}
private var comparator: Comparator<HabitGroup>? =
getComposedComparatorByOrder(primaryOrder, secondaryOrder)
private var parent: MemoryHabitGroupList? = null
constructor() : super()
constructor(
matcher: HabitMatcher,
comparator: Comparator<HabitGroup>?,
parent: MemoryHabitGroupList
) : super(matcher) {
this.parent = parent
this.comparator = comparator
primaryOrder = parent.primaryOrder
secondaryOrder = parent.secondaryOrder
parent.observable.addListener { loadFromParent() }
loadFromParent()
}
@Synchronized
@Throws(IllegalArgumentException::class)
override fun add(habitGroup: HabitGroup) {
throwIfHasParent()
require(!list.contains(habitGroup)) { "habit already added" }
val id = habitGroup.id
if (id != null && getById(id) != null) throw RuntimeException("duplicate id")
if (id == null) habitGroup.id = list.size.toLong()
list.addLast(habitGroup)
resort()
}
@Synchronized
override fun getById(id: Long): HabitGroup? {
for (h in list) {
checkNotNull(h.id)
if (h.id == id) return h
}
return null
}
@Synchronized
override fun getByUUID(uuid: String?): HabitGroup? {
for (h in list) if (Objects.requireNonNull(h.uuid) == uuid) return h
return null
}
@Synchronized
override fun getByPosition(position: Int): HabitGroup {
return list[position]
}
@Synchronized
override fun getFiltered(matcher: HabitMatcher?): HabitGroupList {
return MemoryHabitGroupList(matcher!!, comparator, this)
}
private fun getComposedComparatorByOrder(
firstOrder: Order,
secondOrder: Order?
): Comparator<HabitGroup> {
return Comparator { h1: HabitGroup, h2: HabitGroup ->
val firstResult = getComparatorByOrder(firstOrder).compare(h1, h2)
if (firstResult != 0 || secondOrder == null) {
return@Comparator firstResult
}
getComparatorByOrder(secondOrder).compare(h1, h2)
}
}
private fun getComparatorByOrder(order: Order): Comparator<HabitGroup> {
val nameComparatorAsc = Comparator<HabitGroup> { habit1, habit2 ->
habit1.name.compareTo(habit2.name)
}
val nameComparatorDesc =
Comparator { h1: HabitGroup, h2: HabitGroup -> nameComparatorAsc.compare(h2, h1) }
val colorComparatorAsc = Comparator<HabitGroup> { (color1), (color2) ->
color1.compareTo(color2)
}
val colorComparatorDesc =
Comparator { h1: HabitGroup, h2: HabitGroup -> colorComparatorAsc.compare(h2, h1) }
val scoreComparatorDesc =
Comparator<HabitGroup> { habit1, habit2 ->
val today = getTodayWithOffset()
habit1.scores[today].value.compareTo(habit2.scores[today].value)
}
val scoreComparatorAsc =
Comparator { h1: HabitGroup, h2: HabitGroup -> scoreComparatorDesc.compare(h2, h1) }
val positionComparator =
Comparator<HabitGroup> { habit1, habit2 -> habit1.position.compareTo(habit2.position) }
val statusComparatorDesc = Comparator { h1: HabitGroup, h2: HabitGroup ->
if (h1.isCompletedToday() != h2.isCompletedToday()) {
return@Comparator if (h1.isCompletedToday()) -1 else 1
}
val today = getTodayWithOffset()
val v1 = h1.scores[today].value
val v2 = h2.scores[today].value
v2.compareTo(v1)
}
val statusComparatorAsc =
Comparator { h1: HabitGroup, h2: HabitGroup -> statusComparatorDesc.compare(h2, h1) }
return when {
order === Order.BY_POSITION -> positionComparator
order === Order.BY_NAME_ASC -> nameComparatorAsc
order === Order.BY_NAME_DESC -> nameComparatorDesc
order === Order.BY_COLOR_ASC -> colorComparatorAsc
order === Order.BY_COLOR_DESC -> colorComparatorDesc
order === Order.BY_SCORE_DESC -> scoreComparatorDesc
order === Order.BY_SCORE_ASC -> scoreComparatorAsc
order === Order.BY_STATUS_DESC -> statusComparatorDesc
order === Order.BY_STATUS_ASC -> statusComparatorAsc
else -> throw IllegalStateException()
}
}
@Synchronized
override fun indexOf(h: HabitGroup): Int {
return list.indexOf(h)
}
@Synchronized
override fun iterator(): Iterator<HabitGroup> {
return ArrayList(list).iterator()
}
@Synchronized
override fun remove(h: HabitGroup) {
throwIfHasParent()
list.remove(h)
observable.notifyListeners()
}
@Synchronized
override fun reorder(from: HabitGroup, to: HabitGroup) {
throwIfHasParent()
check(!(primaryOrder !== Order.BY_POSITION)) { "cannot reorder automatically sorted list" }
require(indexOf(from) >= 0) { "list does not contain (from) habit" }
val toPos = indexOf(to)
require(toPos >= 0) { "list does not contain (to) habit" }
list.remove(from)
list.add(toPos, from)
var position = 0
for (h in list) h.position = position++
observable.notifyListeners()
}
@Synchronized
override fun size(): Int {
return list.size
}
@Synchronized
override fun update(habitGroups: List<HabitGroup>) {
resort()
}
private fun throwIfHasParent() {
check(parent == null) {
"Filtered lists cannot be modified directly. " +
"You should modify the parent list instead."
}
}
@Synchronized
private fun loadFromParent() {
checkNotNull(parent)
list.clear()
for (h in parent!!) if (filter.matches(h)) list.add(h)
resort()
}
@Synchronized
override fun resort() {
for (hgr in list) {
hgr.habitList.primaryOrder = primaryOrder
hgr.habitList.secondaryOrder = secondaryOrder
hgr.habitList.resort()
hgr.habitGroupList.primaryOrder = primaryOrder
hgr.habitGroupList.secondaryOrder = secondaryOrder
hgr.habitGroupList.resort()
}
if (comparator != null) list.sortWith(comparator!!)
observable.notifyListeners()
}
}

@ -27,8 +27,10 @@ class MemoryModelFactory : ModelFactory {
override fun buildComputedEntries() = EntryList()
override fun buildOriginalEntries() = EntryList()
override fun buildHabitList() = MemoryHabitList()
override fun buildHabitGroupList() = MemoryHabitGroupList()
override fun buildScoreList() = ScoreList()
override fun buildStreakList() = StreakList()
override fun buildHabitListRepository() = throw NotImplementedError()
override fun buildRepetitionListRepository() = throw NotImplementedError()
override fun buildHabitGroupListRepository() = throw NotImplementedError()
}

@ -25,6 +25,7 @@ import org.isoron.uhabits.core.models.ModelFactory
import org.isoron.uhabits.core.models.ScoreList
import org.isoron.uhabits.core.models.StreakList
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
import org.isoron.uhabits.core.models.sqlite.records.HabitGroupRecord
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
import javax.inject.Inject
@ -38,6 +39,8 @@ class SQLModelFactory
override fun buildOriginalEntries() = SQLiteEntryList(database)
override fun buildComputedEntries() = EntryList()
override fun buildHabitList() = SQLiteHabitList(this)
override fun buildHabitGroupList() = SQLiteHabitGroupList(this)
override fun buildScoreList() = ScoreList()
override fun buildStreakList() = StreakList()
@ -46,4 +49,7 @@ class SQLModelFactory
override fun buildRepetitionListRepository() =
Repository(EntryRecord::class.java, database)
override fun buildHabitGroupListRepository() =
Repository(HabitGroupRecord::class.java, database)
}

@ -0,0 +1,200 @@
package org.isoron.uhabits.core.models.sqlite
import org.isoron.uhabits.core.database.Repository
import org.isoron.uhabits.core.models.HabitGroup
import org.isoron.uhabits.core.models.HabitGroupList
import org.isoron.uhabits.core.models.HabitList.Order
import org.isoron.uhabits.core.models.HabitMatcher
import org.isoron.uhabits.core.models.ModelFactory
import org.isoron.uhabits.core.models.memory.MemoryHabitGroupList
import org.isoron.uhabits.core.models.sqlite.records.HabitGroupRecord
import javax.inject.Inject
/**
* Implementation of a [HabitGroupList] that is backed by SQLite.
*/
class SQLiteHabitGroupList @Inject constructor(private val modelFactory: ModelFactory) : HabitGroupList() {
private val repository: Repository<HabitGroupRecord> = modelFactory.buildHabitGroupListRepository()
private val list: MemoryHabitGroupList = MemoryHabitGroupList()
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.buildHabitGroup()
rec.copyTo(h)
list.add(h)
}
if (shouldRebuildOrder) rebuildOrder()
}
@Synchronized
override fun add(habitGroup: HabitGroup) {
loadRecords()
habitGroup.position = size()
val record = HabitGroupRecord()
record.copyFrom(habitGroup)
repository.save(record)
habitGroup.id = record.id
list.add(habitGroup)
observable.notifyListeners()
}
@Synchronized
override fun getById(id: Long): HabitGroup? {
loadRecords()
return list.getById(id)
}
@Synchronized
override fun getByUUID(uuid: String?): HabitGroup? {
loadRecords()
return list.getByUUID(uuid)
}
@Synchronized
override fun getByPosition(position: Int): HabitGroup {
loadRecords()
return list.getByPosition(position)
}
@Synchronized
override fun getFiltered(matcher: HabitMatcher?): HabitGroupList {
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: HabitGroup): Int {
loadRecords()
return list.indexOf(h)
}
@Synchronized
override fun iterator(): Iterator<HabitGroup> {
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: HabitGroup) {
loadRecords()
list.remove(h)
val record = repository.find(
h.id!!
) ?: throw RuntimeException("habit not in database")
repository.executeAsTransaction {
repository.remove(record)
}
rebuildOrder()
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: HabitGroup, to: HabitGroup) {
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(habitGroups: List<HabitGroup>) {
loadRecords()
list.update(habitGroups)
for (h in habitGroups) {
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
}
}

@ -0,0 +1,100 @@
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.HabitGroup
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.requireNonNull
/**
* The SQLite database record corresponding to a [HabitGroup].
*/
@Table(name = "habitgroups")
class HabitGroupRecord {
@field:Column
var description: String? = null
@field:Column
var question: String? = null
@field:Column
var name: String? = 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 id: Long? = null
@field:Column
var uuid: String? = null
@field:Column(name = "parent_id")
var parentID: Long? = null
@field:Column(name = "parent_uuid")
var parentUUID: String? = null
fun copyFrom(model: HabitGroup) {
id = model.id
name = model.name
description = model.description
highlight = 0
color = model.color.paletteIndex
archived = if (model.isArchived) 1 else 0
position = model.position
question = model.question
uuid = model.uuid
reminderDays = 0
reminderMin = null
reminderHour = null
parentID = model.parentID
parentUUID = model.parentUUID
if (model.hasReminder()) {
val reminder = model.reminder
reminderHour = requireNonNull(reminder)!!.hour
reminderMin = reminder!!.minute
reminderDays = reminder.days.toInteger()
}
}
fun copyTo(habitGroup: HabitGroup) {
habitGroup.id = id
habitGroup.name = name!!
habitGroup.description = description!!
habitGroup.question = question!!
habitGroup.color = PaletteColor(color!!)
habitGroup.isArchived = archived != 0
habitGroup.position = position!!
habitGroup.uuid = uuid
habitGroup.parentID = parentID
habitGroup.parentUUID = parentUUID
if (reminderHour != null && reminderMin != null) {
habitGroup.reminder = Reminder(
reminderHour!!,
reminderMin!!,
WeekdayList(reminderDays!!)
)
}
}
}

@ -95,6 +95,12 @@ class HabitRecord {
@field:Column
var uuid: String? = null
@field:Column(name = "parent_id")
var parentID: Long? = null
@field:Column(name = "parent_uuid")
var parentUUID: String? = null
fun copyFrom(model: Habit) {
id = model.id
name = model.name
@ -109,6 +115,8 @@ class HabitRecord {
position = model.position
question = model.question
uuid = model.uuid
parentID = model.parentID
parentUUID = model.parentUUID
val (numerator, denominator) = model.frequency
freqNum = numerator
freqDen = denominator
@ -140,6 +148,8 @@ class HabitRecord {
habit.unit = unit!!
habit.position = position!!
habit.uuid = uuid
habit.parentID = parentID
habit.parentUUID = parentUUID
if (reminderHour != null && reminderMin != null) {
habit.reminder = Reminder(
reminderHour!!,

@ -1,2 +1,21 @@
alter table Habits add column skip_days integer not null default 0;
alter table Habits add column skip_days_list integer not null default 0;
alter table Habits add column skip_days_list integer not null default 0;
alter table Habits add column parent_id integer;
alter table Habits add column parent_uuid text;
create table HabitGroups (
id integer primary key autoincrement,
archived integer,
color integer,
description text not null default "",
highlight integer,
name text,
position integer,
reminder_days integer not null default 127,
reminder_hour integer,
reminder_min integer,
question text not null default "",
uuid text,
parent_id integer,
parent_uuid integer
);
Loading…
Cancel
Save