mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Reformat all Kotlin files with ktlint; add check to build script
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core
|
||||
|
||||
import javax.inject.*
|
||||
import javax.inject.Scope
|
||||
|
||||
@Scope
|
||||
annotation class AppScope
|
||||
annotation class AppScope
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.commands
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
|
||||
data class ArchiveHabitsCommand(
|
||||
val habitList: HabitList,
|
||||
val selected: List<Habit>,
|
||||
val habitList: HabitList,
|
||||
val selected: List<Habit>,
|
||||
) : Command {
|
||||
override fun run() {
|
||||
for (h in selected) h.isArchived = true
|
||||
habitList.update(selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,17 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.commands
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
|
||||
data class ChangeHabitColorCommand(
|
||||
val habitList: HabitList,
|
||||
val selected: List<Habit>,
|
||||
val newColor: PaletteColor,
|
||||
val habitList: HabitList,
|
||||
val selected: List<Habit>,
|
||||
val newColor: PaletteColor,
|
||||
) : Command {
|
||||
override fun run() {
|
||||
for (h in selected) h.color = newColor
|
||||
habitList.update(selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,4 @@ package org.isoron.uhabits.core.commands
|
||||
|
||||
interface Command {
|
||||
fun run()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,27 +18,30 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.commands
|
||||
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import java.util.*
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.tasks.Task
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import java.util.LinkedList
|
||||
import javax.inject.Inject
|
||||
|
||||
@AppScope
|
||||
open class CommandRunner
|
||||
@Inject constructor(
|
||||
private val taskRunner: TaskRunner,
|
||||
private val taskRunner: TaskRunner,
|
||||
) {
|
||||
private val listeners: LinkedList<Listener> = LinkedList()
|
||||
|
||||
open fun run(command: Command) {
|
||||
taskRunner.execute(object : Task {
|
||||
override fun doInBackground() {
|
||||
command.run()
|
||||
taskRunner.execute(
|
||||
object : Task {
|
||||
override fun doInBackground() {
|
||||
command.run()
|
||||
}
|
||||
override fun onPostExecute() {
|
||||
notifyListeners(command)
|
||||
}
|
||||
}
|
||||
override fun onPostExecute() {
|
||||
notifyListeners(command)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fun addListener(l: Listener) {
|
||||
@@ -56,4 +59,4 @@ open class CommandRunner
|
||||
interface Listener {
|
||||
fun onCommandFinished(command: Command)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,16 +18,18 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.commands
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.ModelFactory
|
||||
|
||||
data class CreateHabitCommand(
|
||||
val modelFactory: ModelFactory,
|
||||
val habitList: HabitList,
|
||||
val model: Habit,
|
||||
val modelFactory: ModelFactory,
|
||||
val habitList: HabitList,
|
||||
val model: Habit,
|
||||
) : Command {
|
||||
override fun run() {
|
||||
val habit = modelFactory.buildHabit()
|
||||
habit.copyFrom(model)
|
||||
habitList.add(habit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,16 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.commands
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
|
||||
data class CreateRepetitionCommand(
|
||||
val habitList: HabitList,
|
||||
val habit: Habit,
|
||||
val timestamp: Timestamp,
|
||||
val value: Int,
|
||||
val habitList: HabitList,
|
||||
val habit: Habit,
|
||||
val timestamp: Timestamp,
|
||||
val value: Int,
|
||||
) : Command {
|
||||
override fun run() {
|
||||
val entries = habit.originalEntries
|
||||
@@ -32,4 +35,4 @@ data class CreateRepetitionCommand(
|
||||
habit.recompute()
|
||||
habitList.resort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.commands
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
|
||||
data class DeleteHabitsCommand(
|
||||
val habitList: HabitList,
|
||||
val selected: List<Habit>,
|
||||
val habitList: HabitList,
|
||||
val selected: List<Habit>,
|
||||
) : Command {
|
||||
override fun run() {
|
||||
for (h in selected) habitList.remove(h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.commands
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.HabitNotFoundException
|
||||
|
||||
data class EditHabitCommand(
|
||||
val habitList: HabitList,
|
||||
val habitId: Long,
|
||||
val modified: Habit,
|
||||
val habitList: HabitList,
|
||||
val habitId: Long,
|
||||
val modified: Habit,
|
||||
) : Command {
|
||||
override fun run() {
|
||||
val habit = habitList.getById(habitId) ?: throw HabitNotFoundException()
|
||||
@@ -32,4 +34,4 @@ data class EditHabitCommand(
|
||||
habit.observable.notifyListeners()
|
||||
habit.recompute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.commands
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
|
||||
data class UnarchiveHabitsCommand(
|
||||
val habitList: HabitList,
|
||||
val selected: List<Habit>,
|
||||
val habitList: HabitList,
|
||||
val selected: List<Habit>,
|
||||
) : Command {
|
||||
override fun run() {
|
||||
for (h in selected) h.isArchived = false
|
||||
habitList.update(selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Column(val name: String = "")
|
||||
annotation class Column(val name: String = "")
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
import java.io.*
|
||||
import java.io.Closeable
|
||||
|
||||
interface Cursor : Closeable {
|
||||
|
||||
@@ -59,4 +59,4 @@ interface Cursor : Closeable {
|
||||
* column has index zero.
|
||||
*/
|
||||
fun getString(index: Int): String?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
import java.io.*
|
||||
import java.io.File
|
||||
|
||||
interface Database {
|
||||
|
||||
@@ -32,10 +32,10 @@ interface Database {
|
||||
}
|
||||
|
||||
fun update(
|
||||
tableName: String,
|
||||
values: Map<String, Any?>,
|
||||
where: String,
|
||||
vararg params: String,
|
||||
tableName: String,
|
||||
values: Map<String, Any?>,
|
||||
where: String,
|
||||
vararg params: String,
|
||||
): Int
|
||||
|
||||
fun insert(tableName: String, values: Map<String, Any?>): Long?
|
||||
@@ -59,4 +59,4 @@ interface Database {
|
||||
interface ProcessCallback {
|
||||
fun process(cursor: Cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
import java.io.*
|
||||
import java.io.File
|
||||
|
||||
interface DatabaseOpener {
|
||||
fun open(file: File): Database?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
import java.sql.*
|
||||
import java.sql.ResultSet
|
||||
import java.sql.SQLException
|
||||
|
||||
class JdbcCursor(private val resultSet: ResultSet) : Cursor {
|
||||
override fun close() {
|
||||
@@ -74,4 +75,4 @@ class JdbcCursor(private val resultSet: ResultSet) : Cursor {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
import org.apache.commons.lang3.*
|
||||
import java.io.*
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.sql.*
|
||||
import java.util.*
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.io.File
|
||||
import java.sql.Connection
|
||||
import java.sql.PreparedStatement
|
||||
import java.sql.SQLException
|
||||
import java.sql.Types
|
||||
import java.util.ArrayList
|
||||
|
||||
class JdbcDatabase(private val connection: Connection) : Database {
|
||||
private var transactionSuccessful = false
|
||||
@@ -36,10 +38,10 @@ class JdbcDatabase(private val connection: Connection) : Database {
|
||||
}
|
||||
|
||||
override fun update(
|
||||
tableName: String,
|
||||
values: Map<String, Any?>,
|
||||
where: String,
|
||||
vararg params: String,
|
||||
tableName: String,
|
||||
values: Map<String, Any?>,
|
||||
where: String,
|
||||
vararg params: String,
|
||||
): Int {
|
||||
return try {
|
||||
val fields = ArrayList<String?>()
|
||||
@@ -49,8 +51,12 @@ class JdbcDatabase(private val connection: Connection) : Database {
|
||||
valuesStr.add(value.toString())
|
||||
}
|
||||
valuesStr.addAll(listOf(*params))
|
||||
val query = String.format("update %s set %s where %s", tableName,
|
||||
StringUtils.join(fields, ", "), where)
|
||||
val query = String.format(
|
||||
"update %s set %s where %s",
|
||||
tableName,
|
||||
StringUtils.join(fields, ", "),
|
||||
where
|
||||
)
|
||||
val st = buildStatement(query, valuesStr.toTypedArray())
|
||||
st.executeUpdate()
|
||||
} catch (e: SQLException) {
|
||||
@@ -68,9 +74,12 @@ class JdbcDatabase(private val connection: Connection) : Database {
|
||||
params.add(value)
|
||||
questionMarks.add("?")
|
||||
}
|
||||
val query = String.format("insert into %s(%s) values(%s)", tableName,
|
||||
StringUtils.join(fields, ", "),
|
||||
StringUtils.join(questionMarks, ", "))
|
||||
val query = String.format(
|
||||
"insert into %s(%s) values(%s)",
|
||||
tableName,
|
||||
StringUtils.join(fields, ", "),
|
||||
StringUtils.join(questionMarks, ", ")
|
||||
)
|
||||
val st = buildStatement(query, params.toTypedArray())
|
||||
st.execute()
|
||||
var id: Long? = null
|
||||
@@ -154,4 +163,4 @@ class JdbcDatabase(private val connection: Connection) : Database {
|
||||
|
||||
override val file: File?
|
||||
get() = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.Locale
|
||||
|
||||
class MigrationHelper(
|
||||
private val db: Database,
|
||||
private val db: Database,
|
||||
) {
|
||||
fun migrateTo(newVersion: Int) {
|
||||
try {
|
||||
@@ -46,4 +48,4 @@ class MigrationHelper(
|
||||
if (file.exists()) return FileInputStream(file)
|
||||
throw RuntimeException("resource not found: $fname")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,17 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
import org.apache.commons.lang3.*
|
||||
import org.apache.commons.lang3.tuple.*
|
||||
import java.lang.reflect.*
|
||||
import java.util.*
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair
|
||||
import org.apache.commons.lang3.tuple.Pair
|
||||
import java.lang.reflect.Field
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
import java.util.LinkedList
|
||||
|
||||
class Repository<T>(
|
||||
private val klass: Class<T>,
|
||||
private val db: Database,
|
||||
private val klass: Class<T>,
|
||||
private val db: Database,
|
||||
) {
|
||||
/**
|
||||
* Returns the record that has the id provided. If no record is found, returns null.
|
||||
@@ -249,4 +252,4 @@ class Repository<T>(
|
||||
if (t == null) throw RuntimeException("Table annotation not found")
|
||||
return t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.ArrayList
|
||||
|
||||
internal class Tokenizer(
|
||||
private val mStream: InputStream,
|
||||
private val mStream: InputStream,
|
||||
) {
|
||||
private var mIsNext = false
|
||||
private var mCurrent = 0
|
||||
@@ -127,4 +128,4 @@ object SQLParser {
|
||||
private fun isWhitespace(c: Char): Boolean {
|
||||
return c == '\r' || c == '\n' || c == '\t' || c == ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,4 @@ package org.isoron.uhabits.core.database
|
||||
|
||||
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Table(val name: String, val id: String = "id")
|
||||
annotation class Table(val name: String, val id: String = "id")
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.database
|
||||
|
||||
class UnsupportedDatabaseVersionException : RuntimeException()
|
||||
class UnsupportedDatabaseVersionException : RuntimeException()
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.io
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import java.io.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A GenericImporter decides which implementation of AbstractImporter is able to
|
||||
@@ -28,18 +28,18 @@ import javax.inject.*
|
||||
*/
|
||||
class GenericImporter
|
||||
@Inject constructor(
|
||||
habits: HabitList,
|
||||
loopDBImporter: LoopDBImporter,
|
||||
rewireDBImporter: RewireDBImporter,
|
||||
tickmateDBImporter: TickmateDBImporter,
|
||||
habitBullCSVImporter: HabitBullCSVImporter,
|
||||
habits: HabitList,
|
||||
loopDBImporter: LoopDBImporter,
|
||||
rewireDBImporter: RewireDBImporter,
|
||||
tickmateDBImporter: TickmateDBImporter,
|
||||
habitBullCSVImporter: HabitBullCSVImporter,
|
||||
) : AbstractImporter(habits) {
|
||||
|
||||
var importers: List<AbstractImporter> = listOf(
|
||||
loopDBImporter,
|
||||
rewireDBImporter,
|
||||
tickmateDBImporter,
|
||||
habitBullCSVImporter,
|
||||
loopDBImporter,
|
||||
rewireDBImporter,
|
||||
tickmateDBImporter,
|
||||
habitBullCSVImporter,
|
||||
)
|
||||
|
||||
override fun canHandle(file: File): Boolean {
|
||||
@@ -58,4 +58,4 @@ class GenericImporter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,27 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.io
|
||||
|
||||
import com.opencsv.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import javax.inject.*
|
||||
import com.opencsv.CSVReader
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
import org.isoron.uhabits.core.models.Frequency
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.ModelFactory
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.util.HashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Class that imports data from HabitBull CSV files.
|
||||
*/
|
||||
class HabitBullCSVImporter
|
||||
@Inject constructor(
|
||||
habits: HabitList,
|
||||
private val modelFactory: ModelFactory,
|
||||
habits: HabitList,
|
||||
private val modelFactory: ModelFactory,
|
||||
) : AbstractImporter(habits) {
|
||||
|
||||
override fun canHandle(file: File): Boolean {
|
||||
@@ -68,4 +75,4 @@ class HabitBullCSVImporter
|
||||
h.originalEntries.add(Entry(timestamp, Entry.YES_MANUAL))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
package org.isoron.uhabits.core.io
|
||||
|
||||
import java.lang.Exception
|
||||
|
||||
interface Logging {
|
||||
fun getLogger(name: String): Logger
|
||||
}
|
||||
@@ -38,7 +36,7 @@ class StandardLogging : Logging {
|
||||
}
|
||||
}
|
||||
|
||||
class StandardLogger(val name: String): Logger {
|
||||
class StandardLogger(val name: String) : Logger {
|
||||
override fun info(msg: String) {
|
||||
println("[$name] $msg")
|
||||
}
|
||||
@@ -54,5 +52,4 @@ class StandardLogger(val name: String): Logger {
|
||||
override fun error(exception: Exception) {
|
||||
exception.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,24 +18,34 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.io
|
||||
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.sqlite.records.*
|
||||
import java.io.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.DATABASE_VERSION
|
||||
import org.isoron.uhabits.core.commands.Command
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.commands.CreateHabitCommand
|
||||
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
||||
import org.isoron.uhabits.core.commands.EditHabitCommand
|
||||
import org.isoron.uhabits.core.database.DatabaseOpener
|
||||
import org.isoron.uhabits.core.database.MigrationHelper
|
||||
import org.isoron.uhabits.core.database.Repository
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.ModelFactory
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
|
||||
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Class that imports data from database files exported by Loop Habit Tracker.
|
||||
*/
|
||||
class LoopDBImporter
|
||||
@Inject constructor(
|
||||
@AppScope val habitList: HabitList,
|
||||
@AppScope val modelFactory: ModelFactory,
|
||||
@AppScope val opener: DatabaseOpener,
|
||||
@AppScope val runner: CommandRunner,
|
||||
@AppScope logging: Logging,
|
||||
@AppScope val habitList: HabitList,
|
||||
@AppScope val modelFactory: ModelFactory,
|
||||
@AppScope val opener: DatabaseOpener,
|
||||
@AppScope val runner: CommandRunner,
|
||||
@AppScope logging: Logging,
|
||||
) : AbstractImporter(habitList) {
|
||||
|
||||
private val logger = logging.getLogger("LoopDBImporter")
|
||||
@@ -99,5 +109,4 @@ class LoopDBImporter
|
||||
|
||||
db.close()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
data class Entry(
|
||||
val timestamp: Timestamp,
|
||||
val value: Int,
|
||||
val timestamp: Timestamp,
|
||||
val value: Int,
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
@@ -65,6 +65,5 @@ data class Entry(
|
||||
else -> NO
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ import org.isoron.uhabits.core.models.Entry.Companion.SKIP
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import java.util.*
|
||||
import javax.annotation.concurrent.*
|
||||
import kotlin.collections.HashMap
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import java.util.ArrayList
|
||||
import java.util.Calendar
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.collections.set
|
||||
import kotlin.math.*
|
||||
import kotlin.math.min
|
||||
|
||||
@ThreadSafe
|
||||
open class EntryList {
|
||||
@@ -90,10 +90,10 @@ open class EntryList {
|
||||
*/
|
||||
@Synchronized
|
||||
open fun groupBy(
|
||||
original: List<Entry>,
|
||||
field: DateUtils.TruncateField,
|
||||
firstWeekday: Int,
|
||||
isNumerical: Boolean,
|
||||
original: List<Entry>,
|
||||
field: DateUtils.TruncateField,
|
||||
firstWeekday: Int,
|
||||
isNumerical: Boolean,
|
||||
): List<Entry> {
|
||||
val truncated = original.map {
|
||||
Entry(it.timestamp.truncate(field, firstWeekday), it.value)
|
||||
@@ -126,9 +126,9 @@ open class EntryList {
|
||||
*/
|
||||
@Synchronized
|
||||
open fun recomputeFrom(
|
||||
originalEntries: EntryList,
|
||||
frequency: Frequency,
|
||||
isNumerical: Boolean,
|
||||
originalEntries: EntryList,
|
||||
frequency: Frequency,
|
||||
isNumerical: Boolean,
|
||||
) {
|
||||
clear()
|
||||
val original = originalEntries.getKnown()
|
||||
@@ -168,9 +168,11 @@ open class EntryList {
|
||||
val map = hashMapOf<Timestamp, Array<Int>>()
|
||||
for ((originalTimestamp, value) in entries) {
|
||||
val weekday = originalTimestamp.weekday
|
||||
val truncatedTimestamp = Timestamp(originalTimestamp.toCalendar().apply {
|
||||
set(Calendar.DAY_OF_MONTH, 1)
|
||||
}.timeInMillis)
|
||||
val truncatedTimestamp = Timestamp(
|
||||
originalTimestamp.toCalendar().apply {
|
||||
set(Calendar.DAY_OF_MONTH, 1)
|
||||
}.timeInMillis
|
||||
)
|
||||
|
||||
var list = map[truncatedTimestamp]
|
||||
if (list == null) {
|
||||
@@ -224,9 +226,9 @@ open class EntryList {
|
||||
@Synchronized
|
||||
open fun getThisWeekValue(firstWeekday: Int, isNumerical: Boolean): Int {
|
||||
return getThisIntervalValue(
|
||||
truncateField = DateUtils.TruncateField.WEEK_NUMBER,
|
||||
firstWeekday = firstWeekday,
|
||||
isNumerical = isNumerical
|
||||
truncateField = DateUtils.TruncateField.WEEK_NUMBER,
|
||||
firstWeekday = firstWeekday,
|
||||
isNumerical = isNumerical
|
||||
)
|
||||
}
|
||||
|
||||
@@ -234,9 +236,9 @@ open class EntryList {
|
||||
@Synchronized
|
||||
open fun getThisMonthValue(isNumerical: Boolean): Int {
|
||||
return getThisIntervalValue(
|
||||
truncateField = DateUtils.TruncateField.MONTH,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = isNumerical
|
||||
truncateField = DateUtils.TruncateField.MONTH,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = isNumerical
|
||||
)
|
||||
}
|
||||
|
||||
@@ -244,9 +246,9 @@ open class EntryList {
|
||||
@Synchronized
|
||||
open fun getThisQuarterValue(isNumerical: Boolean): Int {
|
||||
return getThisIntervalValue(
|
||||
truncateField = DateUtils.TruncateField.QUARTER,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = isNumerical
|
||||
truncateField = DateUtils.TruncateField.QUARTER,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = isNumerical
|
||||
)
|
||||
}
|
||||
|
||||
@@ -254,16 +256,16 @@ open class EntryList {
|
||||
@Synchronized
|
||||
open fun getThisYearValue(isNumerical: Boolean): Int {
|
||||
return getThisIntervalValue(
|
||||
truncateField = DateUtils.TruncateField.YEAR,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = isNumerical
|
||||
truncateField = DateUtils.TruncateField.YEAR,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = isNumerical
|
||||
)
|
||||
}
|
||||
|
||||
private fun getThisIntervalValue(
|
||||
truncateField: DateUtils.TruncateField,
|
||||
firstWeekday: Int,
|
||||
isNumerical: Boolean,
|
||||
truncateField: DateUtils.TruncateField,
|
||||
firstWeekday: Int,
|
||||
isNumerical: Boolean,
|
||||
): Int {
|
||||
val groups: List<Entry> = groupBy(getKnown(), truncateField, firstWeekday, isNumerical)
|
||||
return if (groups.isEmpty()) 0 else groups[0].value
|
||||
@@ -271,7 +273,7 @@ open class EntryList {
|
||||
|
||||
data class Interval(val begin: Timestamp, val center: Timestamp, val end: Timestamp) {
|
||||
val length: Int
|
||||
get() = begin.daysUntil(end) + 1;
|
||||
get() = begin.daysUntil(end) + 1
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,8 +286,8 @@ open class EntryList {
|
||||
*/
|
||||
companion object {
|
||||
fun buildEntriesFromInterval(
|
||||
original: List<Entry>,
|
||||
intervals: List<Interval>,
|
||||
original: List<Entry>,
|
||||
intervals: List<Interval>,
|
||||
): List<Entry> {
|
||||
val result = arrayListOf<Entry>()
|
||||
if (original.isEmpty()) return result
|
||||
@@ -346,16 +348,18 @@ open class EntryList {
|
||||
val gapCenterToEnd = curr.center.daysUntil(curr.end)
|
||||
if (gapNextToCurrent >= 0) {
|
||||
val shift = min(gapCenterToEnd, gapNextToCurrent + 1)
|
||||
intervals[i] = Interval(curr.begin.minus(shift),
|
||||
curr.center,
|
||||
curr.end.minus(shift))
|
||||
intervals[i] = Interval(
|
||||
curr.begin.minus(shift),
|
||||
curr.center,
|
||||
curr.end.minus(shift)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun buildIntervals(
|
||||
freq: Frequency,
|
||||
entries: List<Entry>,
|
||||
freq: Frequency,
|
||||
entries: List<Entry>,
|
||||
): ArrayList<Interval> {
|
||||
val filtered = entries.filter { it.value == YES_MANUAL }
|
||||
val num = freq.numerator
|
||||
@@ -372,4 +376,4 @@ open class EntryList {
|
||||
return intervals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
data class Frequency(
|
||||
var numerator: Int,
|
||||
var denominator: Int,
|
||||
var numerator: Int,
|
||||
var denominator: Int,
|
||||
) {
|
||||
init {
|
||||
if (numerator == denominator) {
|
||||
@@ -46,4 +46,4 @@ data class Frequency(
|
||||
@JvmField
|
||||
val WEEKLY = Frequency(1, 7)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,31 +18,31 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import java.util.*
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import java.util.UUID
|
||||
|
||||
data class Habit(
|
||||
var color: PaletteColor = PaletteColor(8),
|
||||
var description: String = "",
|
||||
var frequency: Frequency = Frequency.DAILY,
|
||||
var id: Long? = null,
|
||||
var isArchived: Boolean = false,
|
||||
var name: String = "",
|
||||
var position: Int = 0,
|
||||
var question: String = "",
|
||||
var reminder: Reminder? = null,
|
||||
var targetType: Int = AT_LEAST,
|
||||
var targetValue: Double = 0.0,
|
||||
var type: Int = YES_NO_HABIT,
|
||||
var unit: String = "",
|
||||
var uuid: String? = null,
|
||||
val computedEntries: EntryList,
|
||||
val originalEntries: EntryList,
|
||||
val scores: ScoreList,
|
||||
val streaks: StreakList,
|
||||
var color: PaletteColor = PaletteColor(8),
|
||||
var description: String = "",
|
||||
var frequency: Frequency = Frequency.DAILY,
|
||||
var id: Long? = null,
|
||||
var isArchived: Boolean = false,
|
||||
var name: String = "",
|
||||
var position: Int = 0,
|
||||
var question: String = "",
|
||||
var reminder: Reminder? = null,
|
||||
var targetType: Int = AT_LEAST,
|
||||
var targetValue: Double = 0.0,
|
||||
var type: Int = YES_NO_HABIT,
|
||||
var unit: String = "",
|
||||
var uuid: String? = null,
|
||||
val computedEntries: EntryList,
|
||||
val originalEntries: EntryList,
|
||||
val scores: ScoreList,
|
||||
val streaks: StreakList,
|
||||
) {
|
||||
init {
|
||||
if(uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "");
|
||||
if (uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "")
|
||||
}
|
||||
|
||||
var observable = ModelObservable()
|
||||
@@ -71,9 +71,9 @@ data class Habit(
|
||||
|
||||
fun recompute() {
|
||||
computedEntries.recomputeFrom(
|
||||
originalEntries = originalEntries,
|
||||
frequency = frequency,
|
||||
isNumerical = isNumerical,
|
||||
originalEntries = originalEntries,
|
||||
frequency = frequency,
|
||||
isNumerical = isNumerical,
|
||||
)
|
||||
|
||||
val to = DateUtils.getTodayWithOffset().plus(30)
|
||||
@@ -82,18 +82,18 @@ data class Habit(
|
||||
if (from.isNewerThan(to)) from = to
|
||||
|
||||
scores.recompute(
|
||||
frequency = frequency,
|
||||
isNumerical = isNumerical,
|
||||
targetValue = targetValue,
|
||||
computedEntries = computedEntries,
|
||||
from = from,
|
||||
to = to,
|
||||
frequency = frequency,
|
||||
isNumerical = isNumerical,
|
||||
targetValue = targetValue,
|
||||
computedEntries = computedEntries,
|
||||
from = from,
|
||||
to = to,
|
||||
)
|
||||
|
||||
streaks.recompute(
|
||||
computedEntries,
|
||||
from,
|
||||
to,
|
||||
computedEntries,
|
||||
from,
|
||||
to,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -160,4 +160,4 @@ data class Habit(
|
||||
const val NUMBER_HABIT = 1
|
||||
const val YES_NO_HABIT = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
data class HabitMatcher(
|
||||
val isArchivedAllowed: Boolean = false,
|
||||
val isReminderRequired: Boolean = false,
|
||||
val isCompletedAllowed: Boolean = true,
|
||||
val isArchivedAllowed: Boolean = false,
|
||||
val isReminderRequired: Boolean = false,
|
||||
val isCompletedAllowed: Boolean = true,
|
||||
) {
|
||||
fun matches(habit: Habit): Boolean {
|
||||
if (!isArchivedAllowed && habit.isArchived) return false
|
||||
@@ -33,8 +33,8 @@ data class HabitMatcher(
|
||||
companion object {
|
||||
@JvmField
|
||||
val WITH_ALARM = HabitMatcherBuilder()
|
||||
.setArchivedAllowed(true)
|
||||
.setReminderRequired(true)
|
||||
.build()
|
||||
.setArchivedAllowed(true)
|
||||
.setReminderRequired(true)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ class HabitMatcherBuilder {
|
||||
|
||||
fun build(): HabitMatcher {
|
||||
return HabitMatcher(
|
||||
isArchivedAllowed = archivedAllowed,
|
||||
isReminderRequired = reminderRequired,
|
||||
isCompletedAllowed = completedAllowed,
|
||||
isArchivedAllowed = archivedAllowed,
|
||||
isReminderRequired = reminderRequired,
|
||||
isCompletedAllowed = completedAllowed,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -45,4 +45,4 @@ class HabitMatcherBuilder {
|
||||
this.reminderRequired = reminderRequired
|
||||
return this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
class HabitNotFoundException : RuntimeException()
|
||||
class HabitNotFoundException : RuntimeException()
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import org.isoron.uhabits.core.models.sqlite.records.*
|
||||
import org.isoron.uhabits.core.database.Repository
|
||||
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
|
||||
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
|
||||
|
||||
/**
|
||||
* Interface implemented by factories that provide concrete implementations of
|
||||
@@ -31,10 +32,10 @@ interface ModelFactory {
|
||||
val scores = buildScoreList()
|
||||
val streaks = buildStreakList()
|
||||
val habit = Habit(
|
||||
scores = scores,
|
||||
streaks = streaks,
|
||||
originalEntries = buildOriginalEntries(),
|
||||
computedEntries = buildComputedEntries(),
|
||||
scores = scores,
|
||||
streaks = streaks,
|
||||
originalEntries = buildOriginalEntries(),
|
||||
computedEntries = buildComputedEntries(),
|
||||
)
|
||||
return habit
|
||||
}
|
||||
@@ -45,4 +46,4 @@ interface ModelFactory {
|
||||
fun buildStreakList(): StreakList
|
||||
fun buildHabitListRepository(): Repository<HabitRecord>
|
||||
fun buildRepetitionListRepository(): Repository<EntryRecord>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,30 +22,30 @@ package org.isoron.uhabits.core.models
|
||||
data class PaletteColor(val paletteIndex: Int) {
|
||||
fun toCsvColor(): String {
|
||||
return arrayOf(
|
||||
"#D32F2F", // 0 red
|
||||
"#E64A19", // 1 deep orange
|
||||
"#F57C00", // 2 orange
|
||||
"#FF8F00", // 3 amber
|
||||
"#F9A825", // 4 yellow
|
||||
"#AFB42B", // 5 lime
|
||||
"#7CB342", // 6 light green
|
||||
"#388E3C", // 7 green
|
||||
"#00897B", // 8 teal
|
||||
"#00ACC1", // 9 cyan
|
||||
"#039BE5", // 10 light blue
|
||||
"#1976D2", // 11 blue
|
||||
"#303F9F", // 12 indigo
|
||||
"#5E35B1", // 13 deep purple
|
||||
"#8E24AA", // 14 purple
|
||||
"#D81B60", // 15 pink
|
||||
"#5D4037", // 16 brown
|
||||
"#303030", // 17 dark grey
|
||||
"#757575", // 18 grey
|
||||
"#aaaaaa" // 19 light grey
|
||||
"#D32F2F", // 0 red
|
||||
"#E64A19", // 1 deep orange
|
||||
"#F57C00", // 2 orange
|
||||
"#FF8F00", // 3 amber
|
||||
"#F9A825", // 4 yellow
|
||||
"#AFB42B", // 5 lime
|
||||
"#7CB342", // 6 light green
|
||||
"#388E3C", // 7 green
|
||||
"#00897B", // 8 teal
|
||||
"#00ACC1", // 9 cyan
|
||||
"#039BE5", // 10 light blue
|
||||
"#1976D2", // 11 blue
|
||||
"#303F9F", // 12 indigo
|
||||
"#5E35B1", // 13 deep purple
|
||||
"#8E24AA", // 14 purple
|
||||
"#D81B60", // 15 pink
|
||||
"#5D4037", // 16 brown
|
||||
"#303030", // 17 dark grey
|
||||
"#757575", // 18 grey
|
||||
"#aaaaaa" // 19 light grey
|
||||
)[paletteIndex]
|
||||
}
|
||||
|
||||
fun compareTo(other: PaletteColor): Int {
|
||||
return paletteIndex.compareTo(other.paletteIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
|
||||
data class Reminder(
|
||||
val hour: Int,
|
||||
val minute: Int,
|
||||
val days: WeekdayList,
|
||||
val hour: Int,
|
||||
val minute: Int,
|
||||
val days: WeekdayList,
|
||||
) {
|
||||
val timeInMillis: Long
|
||||
get() = DateUtils.getUpcomingTimeInMillis(hour, minute)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
import kotlin.math.*
|
||||
import kotlin.math.sqrt
|
||||
|
||||
data class Score(
|
||||
val timestamp: Timestamp,
|
||||
val value: Double,
|
||||
val timestamp: Timestamp,
|
||||
val value: Double,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -36,9 +36,9 @@ data class Score(
|
||||
*/
|
||||
@JvmStatic
|
||||
fun compute(
|
||||
frequency: Double,
|
||||
previousScore: Double,
|
||||
checkmarkValue: Double,
|
||||
frequency: Double,
|
||||
previousScore: Double,
|
||||
checkmarkValue: Double,
|
||||
): Double {
|
||||
val multiplier = Math.pow(0.5, sqrt(frequency) / 13.0)
|
||||
var score = previousScore * multiplier
|
||||
@@ -46,4 +46,4 @@ data class Score(
|
||||
return score
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
import org.isoron.uhabits.core.models.Score.Companion.compute
|
||||
import java.util.*
|
||||
import javax.annotation.concurrent.*
|
||||
import kotlin.math.*
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.math.min
|
||||
|
||||
@ThreadSafe
|
||||
class ScoreList {
|
||||
@@ -46,8 +47,8 @@ class ScoreList {
|
||||
*/
|
||||
@Synchronized
|
||||
fun getByInterval(
|
||||
fromTimestamp: Timestamp,
|
||||
toTimestamp: Timestamp,
|
||||
fromTimestamp: Timestamp,
|
||||
toTimestamp: Timestamp,
|
||||
): List<Score> {
|
||||
val result: MutableList<Score> = ArrayList()
|
||||
if (fromTimestamp.isNewerThan(toTimestamp)) return result
|
||||
@@ -64,12 +65,12 @@ class ScoreList {
|
||||
*/
|
||||
@Synchronized
|
||||
fun recompute(
|
||||
frequency: Frequency,
|
||||
isNumerical: Boolean,
|
||||
targetValue: Double,
|
||||
computedEntries: EntryList,
|
||||
from: Timestamp,
|
||||
to: Timestamp,
|
||||
frequency: Frequency,
|
||||
isNumerical: Boolean,
|
||||
targetValue: Double,
|
||||
computedEntries: EntryList,
|
||||
from: Timestamp,
|
||||
to: Timestamp,
|
||||
) {
|
||||
map.clear()
|
||||
if (computedEntries.getKnown().isEmpty()) return
|
||||
@@ -116,4 +117,4 @@ class ScoreList {
|
||||
map[timestamp] = Score(timestamp, previousValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
import java.lang.Long.*
|
||||
import java.lang.Long.signum
|
||||
|
||||
data class Streak(
|
||||
val start: Timestamp,
|
||||
val end: Timestamp,
|
||||
val start: Timestamp,
|
||||
val end: Timestamp,
|
||||
) {
|
||||
fun compareLonger(other: Streak): Int {
|
||||
return if (length != other.length) signum(length - other.length.toLong())
|
||||
@@ -35,4 +35,4 @@ data class Streak(
|
||||
|
||||
val length: Int
|
||||
get() = start.daysUntil(end) + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
import javax.annotation.concurrent.*
|
||||
import kotlin.math.*
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.math.min
|
||||
|
||||
@ThreadSafe
|
||||
class StreakList {
|
||||
@@ -35,16 +35,16 @@ class StreakList {
|
||||
|
||||
@Synchronized
|
||||
fun recompute(
|
||||
computedEntries: EntryList,
|
||||
from: Timestamp,
|
||||
to: Timestamp,
|
||||
computedEntries: EntryList,
|
||||
from: Timestamp,
|
||||
to: Timestamp,
|
||||
) {
|
||||
list.clear()
|
||||
val timestamps = computedEntries
|
||||
.getByInterval(from, to)
|
||||
.filter { it.value > 0 }
|
||||
.map { it.timestamp }
|
||||
.toTypedArray()
|
||||
.getByInterval(from, to)
|
||||
.filter { it.value > 0 }
|
||||
.map { it.timestamp }
|
||||
.toTypedArray()
|
||||
|
||||
if (timestamps.isEmpty()) return
|
||||
|
||||
@@ -62,4 +62,4 @@ class StreakList {
|
||||
}
|
||||
list.add(Streak(begin, end))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.models.memory
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.EntryList
|
||||
import org.isoron.uhabits.core.models.ModelFactory
|
||||
import org.isoron.uhabits.core.models.ScoreList
|
||||
import org.isoron.uhabits.core.models.StreakList
|
||||
|
||||
class MemoryModelFactory : ModelFactory {
|
||||
override fun buildComputedEntries() = EntryList()
|
||||
@@ -28,4 +31,4 @@ class MemoryModelFactory : ModelFactory {
|
||||
override fun buildStreakList() = StreakList()
|
||||
override fun buildHabitListRepository() = throw NotImplementedError()
|
||||
override fun buildRepetitionListRepository() = throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,17 +20,22 @@
|
||||
*/
|
||||
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 javax.inject.*
|
||||
import org.isoron.uhabits.core.database.Database
|
||||
import org.isoron.uhabits.core.database.Repository
|
||||
import org.isoron.uhabits.core.models.EntryList
|
||||
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.HabitRecord
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Factory that provides models backed by an SQLite database.
|
||||
*/
|
||||
class SQLModelFactory
|
||||
@Inject constructor(
|
||||
val database: Database,
|
||||
val database: Database,
|
||||
) : ModelFactory {
|
||||
override fun buildOriginalEntries() = SQLiteEntryList(database)
|
||||
override fun buildComputedEntries() = EntryList()
|
||||
@@ -39,8 +44,8 @@ class SQLModelFactory
|
||||
override fun buildStreakList() = StreakList()
|
||||
|
||||
override fun buildHabitListRepository() =
|
||||
Repository(HabitRecord::class.java, database)
|
||||
Repository(HabitRecord::class.java, database)
|
||||
|
||||
override fun buildRepetitionListRepository() =
|
||||
Repository(EntryRecord::class.java, database)
|
||||
}
|
||||
Repository(EntryRecord::class.java, database)
|
||||
}
|
||||
|
||||
@@ -19,10 +19,14 @@
|
||||
|
||||
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.*
|
||||
import org.isoron.uhabits.core.database.Database
|
||||
import org.isoron.uhabits.core.database.Repository
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
import org.isoron.uhabits.core.models.EntryList
|
||||
import org.isoron.uhabits.core.models.Frequency
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
|
||||
class SQLiteEntryList(database: Database) : EntryList() {
|
||||
val repository = Repository(EntryRecord::class.java, database)
|
||||
@@ -32,8 +36,10 @@ class SQLiteEntryList(database: Database) : EntryList() {
|
||||
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())
|
||||
val records = repository.findAll(
|
||||
"where habit = ? order by timestamp",
|
||||
habitId.toString()
|
||||
)
|
||||
for (rec in records) super.add(rec.toEntry())
|
||||
isLoaded = true
|
||||
}
|
||||
@@ -53,9 +59,11 @@ class SQLiteEntryList(database: Database) : EntryList() {
|
||||
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())
|
||||
repository.execSQL(
|
||||
"delete from repetitions where habit = ? and timestamp = ?",
|
||||
habitId.toString(),
|
||||
entry.timestamp.unixTime.toString()
|
||||
)
|
||||
|
||||
// Add new row
|
||||
val record = EntryRecord().apply { copyFrom(entry) }
|
||||
@@ -72,10 +80,10 @@ class SQLiteEntryList(database: Database) : EntryList() {
|
||||
}
|
||||
|
||||
override fun groupBy(
|
||||
original: List<Entry>,
|
||||
field: DateUtils.TruncateField,
|
||||
firstWeekday: Int,
|
||||
isNumerical: Boolean
|
||||
original: List<Entry>,
|
||||
field: DateUtils.TruncateField,
|
||||
firstWeekday: Int,
|
||||
isNumerical: Boolean
|
||||
): List<Entry> {
|
||||
loadRecords()
|
||||
return super.groupBy(original, field, firstWeekday, isNumerical)
|
||||
@@ -87,7 +95,9 @@ class SQLiteEntryList(database: Database) : EntryList() {
|
||||
|
||||
override fun clear() {
|
||||
super.clear()
|
||||
repository.execSQL("delete from repetitions where habit = ?",
|
||||
habitId.toString())
|
||||
repository.execSQL(
|
||||
"delete from repetitions where habit = ?",
|
||||
habitId.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,4 +57,4 @@ interface AbstractSyncServer {
|
||||
* to insufficient server resources or network problems.
|
||||
*/
|
||||
suspend fun getDataVersion(key: String): Long
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,23 @@
|
||||
|
||||
package org.isoron.uhabits.core.sync
|
||||
|
||||
import com.google.common.io.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.apache.commons.codec.binary.*
|
||||
import java.io.*
|
||||
import java.nio.*
|
||||
import java.util.zip.*
|
||||
import javax.crypto.*
|
||||
import javax.crypto.spec.*
|
||||
import com.google.common.io.ByteStreams
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.invoke
|
||||
import org.apache.commons.codec.binary.Base64
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.KeyGenerator
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/**
|
||||
* Encryption key which can be used with [File.encryptToString], [String.decryptToFile],
|
||||
@@ -38,8 +47,8 @@ import javax.crypto.spec.*
|
||||
* Base64-encoded string, use [EncryptionKey.fromBase64].
|
||||
*/
|
||||
class EncryptionKey private constructor(
|
||||
val base64: String,
|
||||
val secretKey: SecretKey
|
||||
val base64: String,
|
||||
val secretKey: SecretKey
|
||||
) {
|
||||
companion object {
|
||||
|
||||
@@ -75,10 +84,10 @@ fun ByteArray.encrypt(key: EncryptionKey): ByteArray {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key.secretKey)
|
||||
val encrypted = cipher.doFinal(this)
|
||||
return ByteBuffer
|
||||
.allocate(16 + encrypted.size)
|
||||
.put(cipher.iv)
|
||||
.put(encrypted)
|
||||
.array()
|
||||
.allocate(16 + encrypted.size)
|
||||
.put(cipher.iv)
|
||||
.put(encrypted)
|
||||
.array()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,4 +140,3 @@ fun File.encryptToString(key: EncryptionKey): String {
|
||||
|
||||
fun ByteArray.encodeBase64(): String = Base64.encodeBase64(this).decodeToString()
|
||||
fun String.decodeBase64(): ByteArray = Base64.decodeBase64(this.toByteArray())
|
||||
|
||||
|
||||
@@ -26,4 +26,4 @@ interface NetworkManager {
|
||||
fun onNetworkAvailable()
|
||||
fun onNetworkLost()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
package org.isoron.uhabits.core.sync
|
||||
|
||||
open class SyncException: RuntimeException()
|
||||
open class SyncException : RuntimeException()
|
||||
|
||||
class KeyNotFoundException: SyncException()
|
||||
class KeyNotFoundException : SyncException()
|
||||
|
||||
class ServiceUnavailable: SyncException()
|
||||
class ServiceUnavailable : SyncException()
|
||||
|
||||
class EditConflictException: SyncException()
|
||||
class EditConflictException : SyncException()
|
||||
|
||||
@@ -19,24 +19,29 @@
|
||||
|
||||
package org.isoron.uhabits.core.sync
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import org.isoron.uhabits.core.io.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import java.io.*
|
||||
import javax.inject.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.invoke
|
||||
import kotlinx.coroutines.launch
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.commands.Command
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.database.Database
|
||||
import org.isoron.uhabits.core.io.Logging
|
||||
import org.isoron.uhabits.core.io.LoopDBImporter
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@AppScope
|
||||
class SyncManager @Inject constructor(
|
||||
val preferences: Preferences,
|
||||
val commandRunner: CommandRunner,
|
||||
val logging: Logging,
|
||||
val networkManager: NetworkManager,
|
||||
val server: AbstractSyncServer,
|
||||
val db: Database,
|
||||
val dbImporter: LoopDBImporter,
|
||||
val preferences: Preferences,
|
||||
val commandRunner: CommandRunner,
|
||||
val logging: Logging,
|
||||
val networkManager: NetworkManager,
|
||||
val server: AbstractSyncServer,
|
||||
val db: Database,
|
||||
val dbImporter: LoopDBImporter,
|
||||
) : Preferences.Listener, CommandRunner.Listener, NetworkManager.Listener {
|
||||
|
||||
private var logger = logging.getLogger("SyncManager")
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
package org.isoron.uhabits.core.ui.callbacks
|
||||
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
|
||||
interface OnToggleCheckmarkListener {
|
||||
fun onToggleEntry(timestamp: Timestamp, value: Int) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,22 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.ui.screens.habits.show
|
||||
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.callbacks.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import kotlin.math.*
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnToggleCheckmarkListener
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class ShowHabitBehavior(
|
||||
private val habitList: HabitList,
|
||||
private val commandRunner: CommandRunner,
|
||||
private val habit: Habit,
|
||||
private val screen: Screen,
|
||||
private val preferences: Preferences,
|
||||
private val habitList: HabitList,
|
||||
private val commandRunner: CommandRunner,
|
||||
private val habit: Habit,
|
||||
private val screen: Screen,
|
||||
private val preferences: Preferences,
|
||||
) : OnToggleCheckmarkListener {
|
||||
|
||||
fun onScoreCardSpinnerPosition(position: Int) {
|
||||
@@ -62,33 +65,35 @@ class ShowHabitBehavior(
|
||||
screen.showNumberPicker(oldValue / 1000.0, habit.unit) { newValue: Double ->
|
||||
val thousands = (newValue * 1000).roundToInt()
|
||||
commandRunner.run(
|
||||
CreateRepetitionCommand(
|
||||
habitList,
|
||||
habit,
|
||||
timestamp,
|
||||
thousands,
|
||||
),
|
||||
CreateRepetitionCommand(
|
||||
habitList,
|
||||
habit,
|
||||
timestamp,
|
||||
thousands,
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
commandRunner.run(
|
||||
CreateRepetitionCommand(
|
||||
habitList,
|
||||
habit,
|
||||
timestamp,
|
||||
value,
|
||||
),
|
||||
CreateRepetitionCommand(
|
||||
habitList,
|
||||
habit,
|
||||
timestamp,
|
||||
value,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interface Screen {
|
||||
fun showNumberPicker(value: Double,
|
||||
unit: String,
|
||||
callback: ListHabitsBehavior.NumberPickerCallback)
|
||||
fun showNumberPicker(
|
||||
value: Double,
|
||||
unit: String,
|
||||
callback: ListHabitsBehavior.NumberPickerCallback
|
||||
)
|
||||
|
||||
fun updateWidgets()
|
||||
fun refresh()
|
||||
fun showHistoryEditorDialog(listener: OnToggleCheckmarkListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,25 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.ui.screens.habits.show
|
||||
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import org.isoron.uhabits.core.ui.callbacks.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.commands.DeleteHabitsCommand
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.tasks.ExportCSVTask
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import java.io.File
|
||||
import java.util.Random
|
||||
|
||||
class ShowHabitMenuBehavior(
|
||||
private val commandRunner: CommandRunner,
|
||||
private val habit: Habit,
|
||||
private val habitList: HabitList,
|
||||
private val screen: Screen,
|
||||
private val system: System,
|
||||
private val taskRunner: TaskRunner,
|
||||
private val commandRunner: CommandRunner,
|
||||
private val habit: Habit,
|
||||
private val habitList: HabitList,
|
||||
private val screen: Screen,
|
||||
private val system: System,
|
||||
private val taskRunner: TaskRunner,
|
||||
) {
|
||||
fun onEditHabit() {
|
||||
screen.showEditHabitScreen(habit)
|
||||
@@ -40,13 +44,15 @@ class ShowHabitMenuBehavior(
|
||||
|
||||
fun onExportCSV() {
|
||||
val outputDir = system.getCSVOutputDir()
|
||||
taskRunner.execute(ExportCSVTask(habitList, listOf(habit), outputDir) { filename: String? ->
|
||||
if (filename != null) {
|
||||
screen.showSendFileScreen(filename)
|
||||
} else {
|
||||
screen.showMessage(Message.COULD_NOT_EXPORT)
|
||||
taskRunner.execute(
|
||||
ExportCSVTask(habitList, listOf(habit), outputDir) { filename: String? ->
|
||||
if (filename != null) {
|
||||
screen.showSendFileScreen(filename)
|
||||
} else {
|
||||
screen.showMessage(Message.COULD_NOT_EXPORT)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fun onDeleteHabit() {
|
||||
@@ -80,7 +86,8 @@ class ShowHabitMenuBehavior(
|
||||
fun showMessage(m: Message?)
|
||||
fun showSendFileScreen(filename: String)
|
||||
fun showDeleteConfirmationScreen(
|
||||
callback: OnConfirmedCallback)
|
||||
callback: OnConfirmedCallback
|
||||
)
|
||||
fun close()
|
||||
fun refresh()
|
||||
}
|
||||
@@ -88,4 +95,4 @@ class ShowHabitMenuBehavior(
|
||||
interface System {
|
||||
fun getCSVOutputDir(): File
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,16 @@
|
||||
|
||||
package org.isoron.uhabits.core.ui.screens.sync
|
||||
|
||||
import org.isoron.uhabits.core.io.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.sync.*
|
||||
import org.isoron.uhabits.core.io.Logging
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.sync.AbstractSyncServer
|
||||
import org.isoron.uhabits.core.sync.EncryptionKey
|
||||
|
||||
class SyncBehavior(
|
||||
val screen: Screen,
|
||||
val preferences: Preferences,
|
||||
val server: AbstractSyncServer,
|
||||
val logging: Logging,
|
||||
val screen: Screen,
|
||||
val preferences: Preferences,
|
||||
val server: AbstractSyncServer,
|
||||
val logging: Logging,
|
||||
) {
|
||||
val logger = logging.getLogger("SyncBehavior")
|
||||
|
||||
@@ -62,4 +63,4 @@ class SyncBehavior(
|
||||
suspend fun showErrorScreen()
|
||||
suspend fun showLink(link: String)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.isoron.uhabits.core.models.sqlite.SQLModelFactory
|
||||
import org.isoron.uhabits.core.test.HabitFixtures
|
||||
import org.junit.Test
|
||||
|
||||
class Version23Test: BaseUnitTest() {
|
||||
class Version23Test : BaseUnitTest() {
|
||||
|
||||
private lateinit var db: Database
|
||||
|
||||
@@ -38,14 +38,14 @@ class Version23Test: BaseUnitTest() {
|
||||
var cursor = db.query("select description from Habits")
|
||||
|
||||
val descriptions = mutableListOf<String?>()
|
||||
while(cursor.moveToNext()){
|
||||
while (cursor.moveToNext()) {
|
||||
descriptions.add(cursor.getString(0))
|
||||
}
|
||||
|
||||
migrateTo23()
|
||||
cursor = db.query("select question from Habits")
|
||||
|
||||
for(i in 0 until descriptions.size){
|
||||
for (i in 0 until descriptions.size) {
|
||||
cursor.moveToNext()
|
||||
MatcherAssert.assertThat(cursor.getString(0), Matchers.equalTo(descriptions[i]))
|
||||
}
|
||||
@@ -56,8 +56,8 @@ class Version23Test: BaseUnitTest() {
|
||||
migrateTo23()
|
||||
val cursor = db.query("select description from Habits")
|
||||
|
||||
while(cursor.moveToNext()){
|
||||
while (cursor.moveToNext()) {
|
||||
MatcherAssert.assertThat(cursor.getString(0), Matchers.equalTo(""))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,16 +19,17 @@
|
||||
|
||||
package org.isoron.uhabits.core.models
|
||||
|
||||
import org.hamcrest.MatcherAssert.*
|
||||
import org.hamcrest.core.IsEqual.*
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.core.IsEqual.equalTo
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.junit.*
|
||||
import java.util.*
|
||||
import kotlin.test.*
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.junit.Test
|
||||
import java.util.Calendar
|
||||
import java.util.Random
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class EntryListTest {
|
||||
@Test
|
||||
@@ -74,13 +75,13 @@ class EntryListTest {
|
||||
entries.add(Entry(today.minus(6), YES_MANUAL))
|
||||
|
||||
val expected = intArrayOf(
|
||||
UNKNOWN, // 1
|
||||
UNKNOWN, // 2
|
||||
YES_MANUAL, // 3
|
||||
UNKNOWN, // 4
|
||||
YES_MANUAL, // 5
|
||||
YES_MANUAL, // 6
|
||||
UNKNOWN, // 7
|
||||
UNKNOWN, // 1
|
||||
UNKNOWN, // 2
|
||||
YES_MANUAL, // 3
|
||||
UNKNOWN, // 4
|
||||
YES_MANUAL, // 5
|
||||
YES_MANUAL, // 6
|
||||
UNKNOWN, // 7
|
||||
)
|
||||
assertThat(entries.getValues(today.minus(7), today.minus(1)), equalTo(expected))
|
||||
}
|
||||
@@ -98,22 +99,21 @@ class EntryListTest {
|
||||
computed.recomputeFrom(original, Frequency(1, 3), isNumerical = false)
|
||||
|
||||
val expected = listOf(
|
||||
Entry(today.minus(2), YES_AUTO),
|
||||
Entry(today.minus(3), YES_AUTO),
|
||||
Entry(today.minus(4), YES_MANUAL),
|
||||
Entry(today.minus(7), YES_AUTO),
|
||||
Entry(today.minus(8), YES_AUTO),
|
||||
Entry(today.minus(9), YES_MANUAL),
|
||||
Entry(today.minus(10), YES_MANUAL),
|
||||
Entry(today.minus(11), YES_AUTO),
|
||||
Entry(today.minus(12), YES_AUTO),
|
||||
Entry(today.minus(2), YES_AUTO),
|
||||
Entry(today.minus(3), YES_AUTO),
|
||||
Entry(today.minus(4), YES_MANUAL),
|
||||
Entry(today.minus(7), YES_AUTO),
|
||||
Entry(today.minus(8), YES_AUTO),
|
||||
Entry(today.minus(9), YES_MANUAL),
|
||||
Entry(today.minus(10), YES_MANUAL),
|
||||
Entry(today.minus(11), YES_AUTO),
|
||||
Entry(today.minus(12), YES_AUTO),
|
||||
)
|
||||
assertEquals(expected, computed.getKnown())
|
||||
|
||||
// Second call should replace all previously added entries
|
||||
computed.recomputeFrom(EntryList(), Frequency(1, 3), isNumerical = false)
|
||||
assertEquals(listOf(), computed.getKnown())
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -129,9 +129,9 @@ class EntryListTest {
|
||||
computed.recomputeFrom(original, Frequency.DAILY, isNumerical = true)
|
||||
|
||||
val expected = listOf(
|
||||
Entry(today.minus(4), 100),
|
||||
Entry(today.minus(9), 200),
|
||||
Entry(today.minus(10), 300),
|
||||
Entry(today.minus(4), 100),
|
||||
Entry(today.minus(9), 200),
|
||||
Entry(today.minus(10), 300),
|
||||
)
|
||||
assertEquals(expected, computed.getKnown())
|
||||
}
|
||||
@@ -139,21 +139,23 @@ class EntryListTest {
|
||||
@Test
|
||||
fun testGroupByNumerical() {
|
||||
val offsets = intArrayOf(
|
||||
0, 5, 9, 15, 17, 21, 23, 27, 28, 35, 41, 45, 47, 53, 56, 62, 70, 73, 78,
|
||||
83, 86, 94, 101, 106, 113, 114, 120, 126, 130, 133, 141, 143, 148, 151, 157, 164,
|
||||
166, 171, 173, 176, 179, 183, 191, 259, 264, 268, 270, 275, 282, 284, 289, 295,
|
||||
302, 306, 310, 315, 323, 325, 328, 335, 343, 349, 351, 353, 357, 359, 360, 367,
|
||||
372, 376, 380, 385, 393, 400, 404, 412, 415, 418, 422, 425, 433, 437, 444, 449,
|
||||
455, 460, 462, 465, 470, 471, 479, 481, 485, 489, 494, 495, 500, 501, 503, 507)
|
||||
0, 5, 9, 15, 17, 21, 23, 27, 28, 35, 41, 45, 47, 53, 56, 62, 70, 73, 78,
|
||||
83, 86, 94, 101, 106, 113, 114, 120, 126, 130, 133, 141, 143, 148, 151, 157, 164,
|
||||
166, 171, 173, 176, 179, 183, 191, 259, 264, 268, 270, 275, 282, 284, 289, 295,
|
||||
302, 306, 310, 315, 323, 325, 328, 335, 343, 349, 351, 353, 357, 359, 360, 367,
|
||||
372, 376, 380, 385, 393, 400, 404, 412, 415, 418, 422, 425, 433, 437, 444, 449,
|
||||
455, 460, 462, 465, 470, 471, 479, 481, 485, 489, 494, 495, 500, 501, 503, 507
|
||||
)
|
||||
|
||||
val values = intArrayOf(
|
||||
230, 306, 148, 281, 134, 285, 104, 158, 325, 236, 303, 210, 118, 124,
|
||||
301, 201, 156, 376, 347, 367, 396, 134, 160, 381, 155, 354, 231, 134, 164, 354,
|
||||
236, 398, 199, 221, 208, 397, 253, 276, 214, 341, 299, 221, 353, 250, 341, 168,
|
||||
374, 205, 182, 217, 297, 321, 104, 237, 294, 110, 136, 229, 102, 271, 250, 294,
|
||||
158, 319, 379, 126, 282, 155, 288, 159, 215, 247, 207, 226, 244, 158, 371, 219,
|
||||
272, 228, 350, 153, 356, 279, 394, 202, 213, 214, 112, 248, 139, 245, 165, 256,
|
||||
370, 187, 208, 231, 341, 312)
|
||||
230, 306, 148, 281, 134, 285, 104, 158, 325, 236, 303, 210, 118, 124,
|
||||
301, 201, 156, 376, 347, 367, 396, 134, 160, 381, 155, 354, 231, 134, 164, 354,
|
||||
236, 398, 199, 221, 208, 397, 253, 276, 214, 341, 299, 221, 353, 250, 341, 168,
|
||||
374, 205, 182, 217, 297, 321, 104, 237, 294, 110, 136, 229, 102, 271, 250, 294,
|
||||
158, 319, 379, 126, 282, 155, 288, 159, 215, 247, 207, 226, 244, 158, 371, 219,
|
||||
272, 228, 350, 153, 356, 279, 394, 202, 213, 214, 112, 248, 139, 245, 165, 256,
|
||||
370, 187, 208, 231, 341, 312
|
||||
)
|
||||
|
||||
val reference = Timestamp.from(2014, Calendar.JUNE, 1)
|
||||
val entries = EntryList()
|
||||
@@ -162,10 +164,10 @@ class EntryListTest {
|
||||
}
|
||||
|
||||
val byMonth = entries.groupBy(
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.MONTH,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = true,
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.MONTH,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = true,
|
||||
)
|
||||
assertThat(byMonth.size, equalTo(17))
|
||||
assertThat(byMonth[0], equalTo(Entry(Timestamp.from(2014, Calendar.JUNE, 1), 230)))
|
||||
@@ -173,10 +175,10 @@ class EntryListTest {
|
||||
assertThat(byMonth[12], equalTo(Entry(Timestamp.from(2013, Calendar.MAY, 1), 1271)))
|
||||
|
||||
val byQuarter = entries.groupBy(
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.QUARTER,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = true,
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.QUARTER,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = true,
|
||||
)
|
||||
assertThat(byQuarter.size, equalTo(6))
|
||||
assertThat(byQuarter[0], equalTo(Entry(Timestamp.from(2014, Calendar.APRIL, 1), 3263)))
|
||||
@@ -184,10 +186,10 @@ class EntryListTest {
|
||||
assertThat(byQuarter[5], equalTo(Entry(Timestamp.from(2013, Calendar.JANUARY, 1), 4975)))
|
||||
|
||||
val byYear = entries.groupBy(
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.YEAR,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = true,
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.YEAR,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = true,
|
||||
)
|
||||
assertThat(byYear.size, equalTo(2))
|
||||
assertThat(byYear[0], equalTo(Entry(Timestamp.from(2014, Calendar.JANUARY, 1), 8227)))
|
||||
@@ -197,12 +199,13 @@ class EntryListTest {
|
||||
@Test
|
||||
fun testGroupByBoolean() {
|
||||
val offsets = intArrayOf(
|
||||
0, 5, 9, 15, 17, 21, 23, 27, 28, 35, 41, 45, 47, 53, 56, 62, 70, 73, 78,
|
||||
83, 86, 94, 101, 106, 113, 114, 120, 126, 130, 133, 141, 143, 148, 151, 157, 164,
|
||||
166, 171, 173, 176, 179, 183, 191, 259, 264, 268, 270, 275, 282, 284, 289, 295,
|
||||
302, 306, 310, 315, 323, 325, 328, 335, 343, 349, 351, 353, 357, 359, 360, 367,
|
||||
372, 376, 380, 385, 393, 400, 404, 412, 415, 418, 422, 425, 433, 437, 444, 449,
|
||||
455, 460, 462, 465, 470, 471, 479, 481, 485, 489, 494, 495, 500, 501, 503, 507)
|
||||
0, 5, 9, 15, 17, 21, 23, 27, 28, 35, 41, 45, 47, 53, 56, 62, 70, 73, 78,
|
||||
83, 86, 94, 101, 106, 113, 114, 120, 126, 130, 133, 141, 143, 148, 151, 157, 164,
|
||||
166, 171, 173, 176, 179, 183, 191, 259, 264, 268, 270, 275, 282, 284, 289, 295,
|
||||
302, 306, 310, 315, 323, 325, 328, 335, 343, 349, 351, 353, 357, 359, 360, 367,
|
||||
372, 376, 380, 385, 393, 400, 404, 412, 415, 418, 422, 425, 433, 437, 444, 449,
|
||||
455, 460, 462, 465, 470, 471, 479, 481, 485, 489, 494, 495, 500, 501, 503, 507
|
||||
)
|
||||
|
||||
val reference = Timestamp.from(2014, Calendar.JUNE, 1)
|
||||
val entries = EntryList()
|
||||
@@ -211,10 +214,10 @@ class EntryListTest {
|
||||
}
|
||||
|
||||
val byMonth = entries.groupBy(
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.MONTH,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = false,
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.MONTH,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = false,
|
||||
)
|
||||
assertThat(byMonth.size, equalTo(17))
|
||||
assertThat(byMonth[0], equalTo(Entry(Timestamp.from(2014, Calendar.JUNE, 1), 1_000)))
|
||||
@@ -222,10 +225,10 @@ class EntryListTest {
|
||||
assertThat(byMonth[12], equalTo(Entry(Timestamp.from(2013, Calendar.MAY, 1), 6_000)))
|
||||
|
||||
val byQuarter = entries.groupBy(
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.QUARTER,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = false,
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.QUARTER,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = false,
|
||||
)
|
||||
assertThat(byQuarter.size, equalTo(6))
|
||||
assertThat(byQuarter[0], equalTo(Entry(Timestamp.from(2014, Calendar.APRIL, 1), 15_000)))
|
||||
@@ -233,10 +236,10 @@ class EntryListTest {
|
||||
assertThat(byQuarter[5], equalTo(Entry(Timestamp.from(2013, Calendar.JANUARY, 1), 20_000)))
|
||||
|
||||
val byYear = entries.groupBy(
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.YEAR,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = false,
|
||||
original = entries.getKnown(),
|
||||
field = DateUtils.TruncateField.YEAR,
|
||||
firstWeekday = Calendar.SATURDAY,
|
||||
isNumerical = false,
|
||||
)
|
||||
assertThat(byYear.size, equalTo(2))
|
||||
assertThat(byYear[0], equalTo(Entry(Timestamp.from(2014, Calendar.JANUARY, 1), 34_000)))
|
||||
@@ -246,30 +249,30 @@ class EntryListTest {
|
||||
@Test
|
||||
fun testAddFromInterval() {
|
||||
val entries = listOf(
|
||||
Entry(day(1), YES_MANUAL),
|
||||
Entry(day(2), NO),
|
||||
Entry(day(4), NO),
|
||||
Entry(day(5), YES_MANUAL),
|
||||
Entry(day(10), YES_MANUAL),
|
||||
Entry(day(11), NO),
|
||||
Entry(day(1), YES_MANUAL),
|
||||
Entry(day(2), NO),
|
||||
Entry(day(4), NO),
|
||||
Entry(day(5), YES_MANUAL),
|
||||
Entry(day(10), YES_MANUAL),
|
||||
Entry(day(11), NO),
|
||||
)
|
||||
val intervals = listOf(
|
||||
EntryList.Interval(day(2), day(2), day(1)),
|
||||
EntryList.Interval(day(6), day(5), day(4)),
|
||||
EntryList.Interval(day(10), day(8), day(8)),
|
||||
EntryList.Interval(day(2), day(2), day(1)),
|
||||
EntryList.Interval(day(6), day(5), day(4)),
|
||||
EntryList.Interval(day(10), day(8), day(8)),
|
||||
)
|
||||
val expected = listOf(
|
||||
Entry(day(1), YES_MANUAL),
|
||||
Entry(day(2), YES_AUTO),
|
||||
Entry(day(3), UNKNOWN),
|
||||
Entry(day(4), YES_AUTO),
|
||||
Entry(day(5), YES_MANUAL),
|
||||
Entry(day(6), YES_AUTO),
|
||||
Entry(day(7), UNKNOWN),
|
||||
Entry(day(8), YES_AUTO),
|
||||
Entry(day(9), YES_AUTO),
|
||||
Entry(day(10), YES_MANUAL),
|
||||
Entry(day(11), NO),
|
||||
Entry(day(1), YES_MANUAL),
|
||||
Entry(day(2), YES_AUTO),
|
||||
Entry(day(3), UNKNOWN),
|
||||
Entry(day(4), YES_AUTO),
|
||||
Entry(day(5), YES_MANUAL),
|
||||
Entry(day(6), YES_AUTO),
|
||||
Entry(day(7), UNKNOWN),
|
||||
Entry(day(8), YES_AUTO),
|
||||
Entry(day(9), YES_AUTO),
|
||||
Entry(day(10), YES_MANUAL),
|
||||
Entry(day(11), NO),
|
||||
)
|
||||
val actual = EntryList.buildEntriesFromInterval(entries, intervals)
|
||||
assertThat(actual, equalTo(expected))
|
||||
@@ -278,16 +281,16 @@ class EntryListTest {
|
||||
@Test
|
||||
fun testSnapIntervalsTogether1() {
|
||||
val original = arrayListOf(
|
||||
EntryList.Interval(day(8), day(8), day(2)),
|
||||
EntryList.Interval(day(12), day(12), day(6)),
|
||||
EntryList.Interval(day(20), day(20), day(14)),
|
||||
EntryList.Interval(day(27), day(27), day(21)),
|
||||
EntryList.Interval(day(8), day(8), day(2)),
|
||||
EntryList.Interval(day(12), day(12), day(6)),
|
||||
EntryList.Interval(day(20), day(20), day(14)),
|
||||
EntryList.Interval(day(27), day(27), day(21)),
|
||||
)
|
||||
val expected = arrayListOf(
|
||||
EntryList.Interval(day(8), day(8), day(2)),
|
||||
EntryList.Interval(day(15), day(12), day(9)),
|
||||
EntryList.Interval(day(22), day(20), day(16)),
|
||||
EntryList.Interval(day(29), day(27), day(23)),
|
||||
EntryList.Interval(day(8), day(8), day(2)),
|
||||
EntryList.Interval(day(15), day(12), day(9)),
|
||||
EntryList.Interval(day(22), day(20), day(16)),
|
||||
EntryList.Interval(day(29), day(27), day(23)),
|
||||
)
|
||||
EntryList.snapIntervalsTogether(original)
|
||||
assertThat(original, equalTo(expected))
|
||||
@@ -296,12 +299,12 @@ class EntryListTest {
|
||||
@Test
|
||||
fun testSnapIntervalsTogether2() {
|
||||
val original = arrayListOf(
|
||||
EntryList.Interval(day(6), day(4), day(0)),
|
||||
EntryList.Interval(day(11), day(8), day(5)),
|
||||
EntryList.Interval(day(6), day(4), day(0)),
|
||||
EntryList.Interval(day(11), day(8), day(5)),
|
||||
)
|
||||
val expected = arrayListOf(
|
||||
EntryList.Interval(day(6), day(4), day(0)),
|
||||
EntryList.Interval(day(13), day(8), day(7)),
|
||||
EntryList.Interval(day(6), day(4), day(0)),
|
||||
EntryList.Interval(day(13), day(8), day(7)),
|
||||
)
|
||||
EntryList.snapIntervalsTogether(original)
|
||||
assertThat(original, equalTo(expected))
|
||||
@@ -310,14 +313,14 @@ class EntryListTest {
|
||||
@Test
|
||||
fun testBuildIntervals1() {
|
||||
val entries = listOf(
|
||||
Entry(day(8), YES_MANUAL),
|
||||
Entry(day(18), YES_MANUAL),
|
||||
Entry(day(23), YES_MANUAL),
|
||||
Entry(day(8), YES_MANUAL),
|
||||
Entry(day(18), YES_MANUAL),
|
||||
Entry(day(23), YES_MANUAL),
|
||||
)
|
||||
val expected = listOf(
|
||||
EntryList.Interval(day(8), day(8), day(2)),
|
||||
EntryList.Interval(day(18), day(18), day(12)),
|
||||
EntryList.Interval(day(23), day(23), day(17)),
|
||||
EntryList.Interval(day(8), day(8), day(2)),
|
||||
EntryList.Interval(day(18), day(18), day(12)),
|
||||
EntryList.Interval(day(23), day(23), day(17)),
|
||||
)
|
||||
val actual = EntryList.buildIntervals(Frequency.WEEKLY, entries)
|
||||
assertThat(actual, equalTo(expected))
|
||||
@@ -326,14 +329,14 @@ class EntryListTest {
|
||||
@Test
|
||||
fun testBuildIntervals2() {
|
||||
val entries = listOf(
|
||||
Entry(day(8), YES_MANUAL),
|
||||
Entry(day(18), YES_MANUAL),
|
||||
Entry(day(23), YES_MANUAL),
|
||||
Entry(day(8), YES_MANUAL),
|
||||
Entry(day(18), YES_MANUAL),
|
||||
Entry(day(23), YES_MANUAL),
|
||||
)
|
||||
val expected = listOf(
|
||||
EntryList.Interval(day(8), day(8), day(8)),
|
||||
EntryList.Interval(day(18), day(18), day(18)),
|
||||
EntryList.Interval(day(23), day(23), day(23)),
|
||||
EntryList.Interval(day(8), day(8), day(8)),
|
||||
EntryList.Interval(day(18), day(18), day(18)),
|
||||
EntryList.Interval(day(23), day(23), day(23)),
|
||||
)
|
||||
val actual = EntryList.buildIntervals(Frequency.DAILY, entries)
|
||||
assertThat(actual, equalTo(expected))
|
||||
@@ -342,32 +345,31 @@ class EntryListTest {
|
||||
@Test
|
||||
fun testBuildIntervals3() {
|
||||
val entries = listOf(
|
||||
Entry(day(8), YES_MANUAL),
|
||||
Entry(day(15), YES_MANUAL),
|
||||
Entry(day(18), YES_MANUAL),
|
||||
Entry(day(22), YES_MANUAL),
|
||||
Entry(day(23), YES_MANUAL),
|
||||
Entry(day(8), YES_MANUAL),
|
||||
Entry(day(15), YES_MANUAL),
|
||||
Entry(day(18), YES_MANUAL),
|
||||
Entry(day(22), YES_MANUAL),
|
||||
Entry(day(23), YES_MANUAL),
|
||||
)
|
||||
val expected = listOf(
|
||||
EntryList.Interval(day(18), day(15), day(12)),
|
||||
EntryList.Interval(day(22), day(18), day(16)),
|
||||
EntryList.Interval(day(23), day(22), day(17)),
|
||||
EntryList.Interval(day(18), day(15), day(12)),
|
||||
EntryList.Interval(day(22), day(18), day(16)),
|
||||
EntryList.Interval(day(23), day(22), day(17)),
|
||||
)
|
||||
val actual = EntryList.buildIntervals(Frequency.TWO_TIMES_PER_WEEK, entries)
|
||||
assertThat(actual, equalTo(expected))
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testBuildIntervals4() {
|
||||
val entries = listOf(
|
||||
Entry(day(10), YES_MANUAL),
|
||||
Entry(day(20), Entry.SKIP),
|
||||
Entry(day(30), YES_MANUAL),
|
||||
Entry(day(10), YES_MANUAL),
|
||||
Entry(day(20), Entry.SKIP),
|
||||
Entry(day(30), YES_MANUAL),
|
||||
)
|
||||
val expected = listOf(
|
||||
EntryList.Interval(day(10), day(10), day(8)),
|
||||
EntryList.Interval(day(30), day(30), day(28)),
|
||||
EntryList.Interval(day(10), day(10), day(8)),
|
||||
EntryList.Interval(day(30), day(30), day(28)),
|
||||
)
|
||||
val actual = EntryList.buildIntervals(Frequency(1, 3), entries)
|
||||
assertThat(actual, equalTo(expected))
|
||||
@@ -413,5 +415,4 @@ class EntryListTest {
|
||||
}
|
||||
|
||||
fun day(offset: Int) = DateUtils.getToday().minus(offset)
|
||||
|
||||
}
|
||||
|
||||
@@ -19,14 +19,18 @@
|
||||
|
||||
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 junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertNotNull
|
||||
import junit.framework.Assert.assertNull
|
||||
import org.isoron.uhabits.core.BaseUnitTest.buildMemoryDatabase
|
||||
import org.isoron.uhabits.core.database.Repository
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
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.*
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class SQLiteEntryListTest {
|
||||
|
||||
@@ -48,27 +52,31 @@ class SQLiteEntryListTest {
|
||||
@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),
|
||||
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.minus(1), value = UNKNOWN),
|
||||
entries.get(today.minus(1)),
|
||||
Entry(timestamp = today, value = 500),
|
||||
entries.get(today),
|
||||
)
|
||||
assertEquals(
|
||||
Entry(timestamp = today.minus(5), value = 300),
|
||||
entries.get(today.minus(5)),
|
||||
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)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -92,14 +100,13 @@ class SQLiteEntryListTest {
|
||||
}
|
||||
|
||||
private fun getByTimestamp(
|
||||
habitId: Int,
|
||||
timestamp: Timestamp,
|
||||
habitId: Int,
|
||||
timestamp: Timestamp,
|
||||
): EntryRecord? {
|
||||
return repository.findFirst(
|
||||
"where habit = ? and timestamp = ?",
|
||||
habitId.toString(),
|
||||
timestamp.unixTime.toString(),
|
||||
"where habit = ? and timestamp = ?",
|
||||
habitId.toString(),
|
||||
timestamp.unixTime.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.sync
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import org.hamcrest.Matchers.*
|
||||
import org.junit.*
|
||||
import org.junit.Assert.*
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.hamcrest.Matchers.greaterThan
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertThat
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.util.Random
|
||||
|
||||
class EncryptionExtTest {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user