Skip to content

Commit

Permalink
Restore Backend class; replace TaskRunner by Kotlin Coroutines
Browse files Browse the repository at this point in the history
  • Loading branch information
iSoron committed Apr 13, 2019
1 parent b0cedde commit 5ea19c9
Show file tree
Hide file tree
Showing 26 changed files with 314 additions and 406 deletions.

This file was deleted.

12 changes: 8 additions & 4 deletions core/src/commonMain/kotlin/org/isoron/platform/io/Database.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

package org.isoron.platform.io

import org.isoron.uhabits.*

interface PreparedStatement {
fun step(): StepResult
fun finalize()
Expand Down Expand Up @@ -63,7 +65,7 @@ fun Database.queryInt(sql: String): Int {

fun Database.nextId(tableName: String): Int {
val stmt = prepareStatement("select seq from sqlite_sequence where name='$tableName'")
if(stmt.step() == StepResult.ROW) {
if (stmt.step() == StepResult.ROW) {
val result = stmt.getInt(0)
stmt.finalize()
return result + 1
Expand All @@ -80,7 +82,9 @@ fun Database.getVersion() = queryInt("pragma user_version")

fun Database.setVersion(v: Int) = run("pragma user_version = $v")

suspend fun Database.migrateTo(newVersion: Int, fileOpener: FileOpener, log: Log) {
suspend fun Database.migrateTo(newVersion: Int,
fileOpener: FileOpener,
log: Log) {
val currentVersion = getVersion()
log.debug("Database", "Current database version: $currentVersion")

Expand All @@ -89,10 +93,10 @@ suspend fun Database.migrateTo(newVersion: Int, fileOpener: FileOpener, log: Log

if (currentVersion > newVersion)
throw RuntimeException("database produced by future version of the application")

begin()
for (v in (currentVersion + 1)..newVersion) {
val sv = if(v < 10) "00$v" else if (v<100) "0$v" else "$v"
val sv = if (v < 10) "00$v" else if (v < 100) "0$v" else "$v"
val filename = "migrations/$sv.sql"
val migrationFile = fileOpener.openResourceFile(filename)
for (line in migrationFile.lines()) {
Expand Down
156 changes: 94 additions & 62 deletions core/src/commonMain/kotlin/org/isoron/uhabits/backend/Backend.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,86 +19,118 @@

package org.isoron.uhabits.backend

import kotlinx.coroutines.*
import org.isoron.platform.concurrency.*
import org.isoron.platform.io.*
import org.isoron.uhabits.*
import org.isoron.uhabits.components.*
import org.isoron.uhabits.i18n.*
import org.isoron.uhabits.models.*
import kotlin.coroutines.*

class Backend(databaseName: String,
databaseOpener: DatabaseOpener,
fileOpener: FileOpener,
localeHelper: LocaleHelper,

open class BackendScope(private val ctx: CoroutineContext,
private val log: Log) : CoroutineScope {

private val job = Job()

private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
log.info("Coroutine", throwable.toString())
}

override val coroutineContext: CoroutineContext
get() = ctx + job + exceptionHandler
}

class Backend(private val databaseName: String,
private val databaseOpener: DatabaseOpener,
private val fileOpener: FileOpener,
private val localeHelper: LocaleHelper,
private val log: Log,
private val taskRunner: TaskRunner) {

// private val database: Database
//
// private val habitsRepository: HabitRepository
//
// private val checkmarkRepository: CheckmarkRepository
//
// private val habits = mutableMapOf<Int, Habit>()
//
// private val checkmarks = mutableMapOf<Habit, CheckmarkList>()
//
// private val scores = mutableMapOf<Habit, ScoreList>()

val mainScreenDataSource: MainScreenDataSource? = null
val strings = localeHelper.getStringsForCurrentLocale()
val preferences: Preferences? = null
private val crCtx: CoroutineContext
) : CoroutineScope by BackendScope(crCtx, log) {


private lateinit var database: Database
private lateinit var habitsRepository: HabitRepository
private lateinit var checkmarkRepository: CheckmarkRepository
lateinit var preferences: Preferences

lateinit var mainScreenDataSource: MainScreenDataSource

private val habits = mutableMapOf<Int, Habit>()
private val checkmarks = mutableMapOf<Habit, CheckmarkList>()
private val scores = mutableMapOf<Habit, ScoreList>()

var strings = localeHelper.getStringsForCurrentLocale()
var theme: Theme = LightTheme()

init {
// val dbFile = fileOpener.openUserFile(databaseName)
// if (!dbFile.exists()) {
// val templateFile = fileOpener.openResourceFile("databases/template.db")
// templateFile.copyTo(dbFile)
// }
// database = databaseOpener.open(dbFile)
// database.migrateTo(LOOP_DATABASE_VERSION, fileOpener, log)
// preferences = Preferences(PreferencesRepository(database))
// habitsRepository = HabitRepository(database)
// checkmarkRepository = CheckmarkRepository(database)
// taskRunner.runInBackground {
// habits.putAll(habitsRepository.findAll())
// for ((key, habit) in habits) {
// val checks = checkmarkRepository.findAll(key)
// checkmarks[habit] = CheckmarkList(habit.frequency, habit.type)
// checkmarks[habit]?.setManualCheckmarks(checks)
// scores[habit] = ScoreList(checkmarks[habit]!!)
// }
// }
// mainScreenDataSource = MainScreenDataSource(preferences,
// habits,
// checkmarks,
// scores,
// taskRunner)
val observable = Observable<Listener>()

fun init() {
launch {
initDatabase()
initRepositories()
initDataSources()
observable.notifyListeners { it.onReady() }
}
}

private fun initRepositories() {
preferences = Preferences(PreferencesRepository(database))
habitsRepository = HabitRepository(database)
checkmarkRepository = CheckmarkRepository(database)
habits.putAll(habitsRepository.findAll())
log.info("Backend", "${habits.size} habits loaded")
for ((key, habit) in habits) {
val checks = checkmarkRepository.findAll(key)
checkmarks[habit] = CheckmarkList(habit.frequency, habit.type)
checkmarks[habit]?.setManualCheckmarks(checks)
scores[habit] = ScoreList(checkmarks[habit]!!)
}
}

private fun initDataSources() {
mainScreenDataSource =
MainScreenDataSource(preferences, habits, checkmarks, scores)
}

private suspend fun initDatabase() {
val dbFile = fileOpener.openUserFile(databaseName)
if (!dbFile.exists()) {
val templateFile = fileOpener.openResourceFile("databases/template.db")
templateFile.copyTo(dbFile)
}
database = databaseOpener.open(dbFile)
database.migrateTo(LOOP_DATABASE_VERSION, fileOpener, log)
}

fun createHabit(habit: Habit) {
// val id = habitsRepository.nextId()
// habit.id = id
// habit.position = habits.size
// habits[id] = habit
// checkmarks[habit] = CheckmarkList(habit.frequency, habit.type)
// habitsRepository.insert(habit)
// mainScreenDataSource.requestData()
val id = habitsRepository.nextId()
habit.id = id
habit.position = habits.size
habits[id] = habit
checkmarks[habit] = CheckmarkList(habit.frequency, habit.type)
habitsRepository.insert(habit)
mainScreenDataSource.requestData()
}

fun deleteHabit(id: Int) {
// habits[id]?.let { habit ->
// habitsRepository.delete(habit)
// habits.remove(id)
// mainScreenDataSource.requestData()
// }
habits[id]?.let { habit ->
habitsRepository.delete(habit)
habits.remove(id)
mainScreenDataSource.requestData()
}
}

fun updateHabit(modified: Habit) {
// habits[modified.id]?.let { existing ->
// modified.position = existing.position
// habitsRepository.update(modified)
// }
habits[modified.id]?.let { existing ->
modified.position = existing.position
habitsRepository.update(modified)
}
}

interface Listener {
fun onReady()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ import org.isoron.uhabits.models.Checkmark.Companion.UNCHECKED
class MainScreenDataSource(val preferences: Preferences,
val habits: MutableMap<Int, Habit>,
val checkmarks: MutableMap<Habit, CheckmarkList>,
val scores: MutableMap<Habit, ScoreList>,
val taskRunner: TaskRunner) {
val scores: MutableMap<Habit, ScoreList>) {

val maxNumberOfButtons = 60
private val today = LocalDate(2019, 3, 30) /* TODO */
Expand All @@ -44,36 +43,32 @@ class MainScreenDataSource(val preferences: Preferences,
}

fun requestData() {
taskRunner.runInBackground {
var filtered = habits.values.toList()
var filtered = habits.values.toList()

if (!preferences.showArchived) {
filtered = filtered.filter { !it.isArchived }
}
if (!preferences.showArchived) {
filtered = filtered.filter { !it.isArchived }
}

val checkmarks = filtered.associate { habit ->
val allValues = checkmarks.getValue(habit).getUntil(today)
if (allValues.size <= maxNumberOfButtons) habit to allValues
else habit to allValues.subList(0, maxNumberOfButtons)
}
val checkmarks = filtered.associate { habit ->
val allValues = checkmarks.getValue(habit).getUntil(today)
if (allValues.size <= maxNumberOfButtons) habit to allValues
else habit to allValues.subList(0, maxNumberOfButtons)
}

if (!preferences.showCompleted) {
filtered = filtered.filter { habit ->
(habit.type == HabitType.BOOLEAN_HABIT && checkmarks.getValue(habit)[0].value == UNCHECKED) ||
(habit.type == HabitType.NUMERICAL_HABIT && checkmarks.getValue(habit)[0].value * 1000 < habit.target)
}
if (!preferences.showCompleted) {
filtered = filtered.filter { habit ->
(habit.type == HabitType.BOOLEAN_HABIT && checkmarks.getValue(habit)[0].value == UNCHECKED) ||
(habit.type == HabitType.NUMERICAL_HABIT && checkmarks.getValue(habit)[0].value * 1000 < habit.target)
}
}

val scores = filtered.associate { habit ->
habit to scores[habit]!!.getAt(today)
}
val scores = filtered.associate { habit ->
habit to scores[habit]!!.getAt(today)
}

taskRunner.runInForeground {
observable.notifyListeners { listener ->
val data = Data(filtered, scores, checkmarks)
listener.onDataChanged(data)
}
}
observable.notifyListeners { listener ->
val data = Data(filtered, scores, checkmarks)
listener.onDataChanged(data)
}
}
}
24 changes: 0 additions & 24 deletions core/src/commonTest/kotlin/org/isoron/BaseTest.kt

This file was deleted.

Loading

0 comments on commit 5ea19c9

Please sign in to comment.