diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 6f78b5c..e0fea0c 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -2,7 +2,9 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.kotlinSerialization) alias(libs.plugins.androidMultiplatformLibrary) + alias(libs.plugins.ksp) } kotlin { @@ -25,8 +27,26 @@ kotlin { jvm() sourceSets { + androidMain.dependencies { + implementation(libs.koin.compose) + implementation(libs.ktor.client.okhttp) + } commonMain.dependencies { + implementation(libs.okio) + implementation(libs.koin.core) + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.contentnegotiation) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.datetime) + implementation(libs.kotlinx.serialization.json) + implementation(libs.kotlinx.serialization.protobuf) + implementation(projects.core) + implementation(projects.core.room) + } + iosMain.dependencies { + implementation(libs.ktor.client.darwin) } } } diff --git a/core/data/client/build.gradle.kts b/core/data/client/build.gradle.kts deleted file mode 100644 index e9848f3..0000000 --- a/core/data/client/build.gradle.kts +++ /dev/null @@ -1,54 +0,0 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.kotlinSerialization) - alias(libs.plugins.androidMultiplatformLibrary) - alias(libs.plugins.ksp) -} - -kotlin { - android { - namespace = "moe.lava.banksia.core.data.client" - compileSdk = libs.versions.android.compileSdk.get().toInt() - - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } - } - - compilerOptions { - freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime") - } - - iosArm64() - iosSimulatorArm64() - - jvm() - - sourceSets { - androidMain.dependencies { - implementation(libs.koin.compose) - implementation(libs.ktor.client.okhttp) - } - commonMain.dependencies { - api(projects.core.data) - - implementation(libs.okio) - implementation(libs.koin.core) - implementation(libs.ktor.client.core) - implementation(libs.ktor.client.contentnegotiation) - implementation(libs.ktor.serialization.kotlinx.json) - implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlinx.datetime) - implementation(libs.kotlinx.serialization.json) - implementation(libs.kotlinx.serialization.protobuf) - - implementation(projects.core) - implementation(projects.core.room) - } - iosMain.dependencies { - implementation(libs.ktor.client.darwin) - } - } -} diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt deleted file mode 100644 index 70a8905..0000000 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ -package moe.lava.banksia.core.data.repositories - -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import moe.lava.banksia.core.data.sources.route.RouteLocalDataSource -import moe.lava.banksia.core.data.sources.route.RouteRemoteDataSource - -internal class ClientRouteRepository internal constructor( - private val local: RouteLocalDataSource, - private val remote: RouteRemoteDataSource, -) : RouteRepository { - private val mutex = Mutex() - override suspend fun getAll() = mutex.withLock { - local - .getAll() - .map { it.asModel() } - .ifEmpty { - remote - .getAll() - .also { local.save(*it.toTypedArray()) } - } - } - - override suspend fun get(id: String) = mutex.withLock { local.get(id)?.asModel() ?: remote.get(id) } -} diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt deleted file mode 100644 index a5fd300..0000000 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt +++ /dev/null @@ -1,22 +0,0 @@ -package moe.lava.banksia.core.data.repositories - -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import moe.lava.banksia.core.data.sources.stop.StopLocalDataSource -import moe.lava.banksia.core.data.sources.stop.StopRemoteDataSource - -internal class ClientStopRepository internal constructor( - private val local: StopLocalDataSource, - private val remote: StopRemoteDataSource, -) : StopRepository { - private val mutex = Mutex() - - override suspend fun get(id: String) = mutex.withLock { local.get(id)?.asModel() ?: remote.get(id) } - override suspend fun getByRoute(id: String) = mutex.withLock { - local - .getByRoute(id) - .map { it.asModel() } - .ifEmpty { null } - ?: remote.getByRoute(id) - } -} diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopTimeRepository.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopTimeRepository.kt deleted file mode 100644 index aea3159..0000000 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopTimeRepository.kt +++ /dev/null @@ -1,16 +0,0 @@ -package moe.lava.banksia.core.data.repositories - -import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource -import moe.lava.banksia.core.data.sources.stoptime.StopTimeRemoteDataSource -import moe.lava.banksia.core.model.StopTimeDated - -internal class ClientStopTimeRepository internal constructor( - private val local: StopTimeLocalDataSource, - private val remote: StopTimeRemoteDataSource, -) : StopTimeRepository { - override suspend fun getForStop(id: String): List { - return local - .getAtStop(id) - .ifEmpty { remote.getAtStop(id) } - } -} diff --git a/core/data/server/build.gradle.kts b/core/data/server/build.gradle.kts deleted file mode 100644 index eaa309b..0000000 --- a/core/data/server/build.gradle.kts +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - alias(libs.plugins.kotlinJvm) - alias(libs.plugins.kotlinSerialization) -} - -kotlin { - compilerOptions { - freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime") - } -} - -dependencies { - implementation(libs.okio) - implementation(libs.koin.core) - implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlinx.datetime) - - api(projects.core.data) - implementation(projects.core) - implementation(projects.core.room) -} diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/DataDiModule.kt similarity index 81% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt rename to core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/DataDiModule.kt index 01e961c..6529a92 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt +++ b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/DataDiModule.kt @@ -8,9 +8,6 @@ import io.ktor.client.plugins.plugin import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json import moe.lava.banksia.core.Constants -import moe.lava.banksia.core.data.repositories.ClientRouteRepository -import moe.lava.banksia.core.data.repositories.ClientStopRepository -import moe.lava.banksia.core.data.repositories.ClientStopTimeRepository import moe.lava.banksia.core.data.repositories.RouteRepository import moe.lava.banksia.core.data.repositories.StopRepository import moe.lava.banksia.core.data.repositories.StopTimeRepository @@ -24,10 +21,9 @@ import moe.lava.banksia.core.room.roomDiModule import moe.lava.banksia.core.util.log import moe.lava.banksia.data.ptv.PtvService import org.koin.core.module.dsl.singleOf -import org.koin.dsl.bind import org.koin.dsl.module -val clientDataDiModule = module { +val dataDiModule = module { includes(roomDiModule) // HTTP Clients @@ -60,7 +56,7 @@ val clientDataDiModule = module { singleOf(::StopTimeRemoteDataSource) // Repositories - singleOf(::ClientRouteRepository) bind RouteRepository::class - singleOf(::ClientStopRepository) bind StopRepository::class - singleOf(::ClientStopTimeRepository) bind StopTimeRepository::class + singleOf(::RouteRepository) + singleOf(::StopRepository) + singleOf(::StopTimeRepository) } diff --git a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt index fbb663f..fdeb95f 100644 --- a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt +++ b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt @@ -1,8 +1,25 @@ package moe.lava.banksia.core.data.repositories -import moe.lava.banksia.core.model.Route +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import moe.lava.banksia.core.data.sources.route.RouteLocalDataSource +import moe.lava.banksia.core.data.sources.route.RouteRemoteDataSource -interface RouteRepository { - suspend fun get(id: String): Route - suspend fun getAll(): List +class RouteRepository internal constructor( + private val local: RouteLocalDataSource, + private val remote: RouteRemoteDataSource, +) { + private val mutex = Mutex() + suspend fun getAll() = mutex.withLock { + local + .getAll() + .map { it.asModel() } + .ifEmpty { + remote + .getAll() + .also { local.save(*it.toTypedArray()) } + } + } + + suspend fun get(id: String) = mutex.withLock { local.get(id)?.asModel() ?: remote.get(id) } } diff --git a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopRepository.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopRepository.kt index c663f89..d83a9cd 100644 --- a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopRepository.kt +++ b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopRepository.kt @@ -1,8 +1,22 @@ package moe.lava.banksia.core.data.repositories -import moe.lava.banksia.core.model.Stop +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import moe.lava.banksia.core.data.sources.stop.StopLocalDataSource +import moe.lava.banksia.core.data.sources.stop.StopRemoteDataSource -interface StopRepository { - suspend fun get(id: String): Stop - suspend fun getByRoute(id: String): List +class StopRepository internal constructor( + private val local: StopLocalDataSource, + private val remote: StopRemoteDataSource, +) { + private val mutex = Mutex() + + suspend fun get(id: String) = mutex.withLock { local.get(id)?.asModel() ?: remote.get(id) } + suspend fun getByRoute(id: String) = mutex.withLock { + local + .getByRoute(id) + .map { it.asModel() } + .ifEmpty { null } + ?: remote.getByRoute(id) + } } diff --git a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt index 87d01ac..34aa570 100644 --- a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt +++ b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt @@ -1,7 +1,16 @@ package moe.lava.banksia.core.data.repositories +import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource +import moe.lava.banksia.core.data.sources.stoptime.StopTimeRemoteDataSource import moe.lava.banksia.core.model.StopTimeDated -interface StopTimeRepository { - suspend fun getForStop(id: String): List +class StopTimeRepository internal constructor( + private val local: StopTimeLocalDataSource, + private val remote: StopTimeRemoteDataSource, +) { + suspend fun getForStop(id: String): List { + return local + .getAtStop(id) + .ifEmpty { remote.getAtStop(id) } + } } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt rename to core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt rename to core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt rename to core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt rename to core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt rename to core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt rename to core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/trip/TripRemoteDataSource.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/trip/TripRemoteDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/trip/TripRemoteDataSource.kt rename to core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/trip/TripRemoteDataSource.kt diff --git a/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.android.kt b/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.android.kt deleted file mode 100644 index 999ee4b..0000000 --- a/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.android.kt +++ /dev/null @@ -1,22 +0,0 @@ -package moe.lava.banksia.core.room - -import android.content.Context -import androidx.room.Room -import org.koin.core.component.KoinComponent -import org.koin.core.component.get -import org.koin.core.component.inject - -actual class DatabaseManager actual constructor() : KoinComponent { - private val ctx by inject() - - actual val database by lazy { - val ctx = get().applicationContext - val dbFile = ctx.getDatabasePath("room.db") - val builder = Room.databaseBuilder( - context = ctx, - name = dbFile.absolutePath, - ) - - Database.build(builder) - } -} diff --git a/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.android.kt b/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.android.kt new file mode 100644 index 0000000..8cd01e6 --- /dev/null +++ b/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.android.kt @@ -0,0 +1,21 @@ +package moe.lava.banksia.core.room + +import android.content.Context +import androidx.room.Room +import androidx.room.RoomDatabase +import org.koin.core.parameter.ParametersHolder +import org.koin.core.scope.Scope + +class AndroidDatabaseBuilder(val ctx: Context) : PlatformDatabaseBuilder { + override fun getBuilder(): RoomDatabase.Builder { + val appContext = ctx.applicationContext + val dbFile = appContext.getDatabasePath("room.db") + return Room.databaseBuilder( + context = appContext, + name = dbFile.absolutePath + ) + } +} + +internal actual fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder = + AndroidDatabaseBuilder(get()) diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.kt deleted file mode 100644 index bfe32a9..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.kt +++ /dev/null @@ -1,7 +0,0 @@ -package moe.lava.banksia.core.room - -import org.koin.core.component.KoinComponent - -expect class DatabaseManager() : KoinComponent { - val database: Database -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.kt index 85c56fc..67d4fd2 100644 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.kt +++ b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.kt @@ -1,18 +1,26 @@ package moe.lava.banksia.core.room -import org.koin.core.module.dsl.singleOf +import androidx.room.RoomDatabase +import org.koin.core.parameter.ParametersHolder +import org.koin.core.scope.Scope import org.koin.dsl.module val roomDiModule = module { - singleOf(::DatabaseManager) - factory { get().database } + single { provideDatabaseBuilder(it) } + single { Database.build(get().getBuilder()) } - factory { get().versionMetadataDao } - factory { get().routeDao } - factory { get().serviceDao } - factory { get().serviceExceptionDao } - factory { get().shapeDao } - factory { get().stopDao } - factory { get().stopTimeDao } - factory { get().tripDao } + single { get().versionMetadataDao } + single { get().routeDao } + single { get().serviceDao } + single { get().serviceExceptionDao } + single { get().shapeDao } + single { get().stopDao } + single { get().stopTimeDao } + single { get().tripDao } } + +internal interface PlatformDatabaseBuilder { + fun getBuilder(): RoomDatabase.Builder +} + +internal expect fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopDao.kt index 7edb560..99f5a2d 100644 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopDao.kt +++ b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopDao.kt @@ -15,7 +15,7 @@ interface StopDao { @Query(""" SELECT * FROM Stop WHERE platformCode <> "" - AND parent IS NULL + AND parent == "" """) suspend fun getAllParentless(): List diff --git a/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.ios.kt b/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.ios.kt deleted file mode 100644 index 34e370e..0000000 --- a/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.ios.kt +++ /dev/null @@ -1,8 +0,0 @@ -package moe.lava.banksia.core.room - -import org.koin.core.component.KoinComponent - -actual class DatabaseManager actual constructor() : KoinComponent { - actual val database: Database - get() = TODO("Not yet implemented") -} diff --git a/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.ios.kt b/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.ios.kt new file mode 100644 index 0000000..3c74852 --- /dev/null +++ b/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.ios.kt @@ -0,0 +1,14 @@ +package moe.lava.banksia.core.room + +import androidx.room.RoomDatabase +import org.koin.core.parameter.ParametersHolder +import org.koin.core.scope.Scope + +class IosDatabaseBuilder() : PlatformDatabaseBuilder { + override fun getBuilder(): RoomDatabase.Builder { + TODO("Not yet implemented") + } +} + +internal actual fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder = + IosDatabaseBuilder() diff --git a/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.jvm.kt b/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.jvm.kt deleted file mode 100644 index 360d2dd..0000000 --- a/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.jvm.kt +++ /dev/null @@ -1,69 +0,0 @@ -package moe.lava.banksia.core.room - -import androidx.room.Room -import androidx.room.RoomDatabase -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import moe.lava.banksia.core.util.error -import org.koin.core.component.KoinComponent -import java.io.File -import kotlin.system.exitProcess - -actual class DatabaseManager : KoinComponent { - private var liveDatabase: Database = Database.build(getBuilder()) - actual val database get() = liveDatabase - - private fun getBuilder(path: String = "./data/room.db"): RoomDatabase.Builder { - val dbFile = File(path) - return Room.databaseBuilder( - name = dbFile.absolutePath, - ).setJournalMode(RoomDatabase.JournalMode.TRUNCATE) - } - - fun makeAlt() = Database.build(getBuilder("./data/room_alt.db")) - - private fun deleteAll(file: File): Boolean { - val r1 = file.takeIf { it.exists() }?.delete() - val r2 = File(file.parentFile, file.name + ".lck").takeIf { it.exists() }?.delete() - val r3 = File(file.parentFile, file.name + "-journal").takeIf { it.exists() }?.delete() - return r1 != false && r2 != false && r3 != false - } - - private fun renameAll(from: File, to: File): Boolean { - val r1 = from.takeIf { it.exists() }?.renameTo(to) - val r2 = File(from.parentFile, from.name + ".lck").takeIf { it.exists() }?.renameTo(File(to.parentFile, to.name + ".lck")) - val r3 = File(from.parentFile, from.name + "-journal").takeIf { it.exists() }?.renameTo(File(to.parentFile, to.name + "-journal")) - return r1 != false && r2 != false && r3 != false - } - - fun swap(scope: CoroutineScope = CoroutineScope(Dispatchers.IO)) { - val live = File("./data/room.db") - val alt = File("./data/room_alt.db") - val old = File("./data/room_old.db") - - if (!renameAll(live, old)) { - error("DatabaseManager", "Failed to rename database from live to old (${live.absolutePath} -> ${old.absolutePath})") - return - } - if (!renameAll(alt, live)) { - error("DatabaseManager", "Failed to rename database from alt to live, trying to undo.. (${alt.absolutePath} -> ${live.absolutePath})") - if (!live.renameTo(old)) { - error("DatabaseManager", "Failed to undo, critical failure, exiting..") - exitProcess(1) - } - return - } - val oldDatabase = liveDatabase - liveDatabase = Database.build(getBuilder()) - - scope.launch { - delay(5000) - if (!deleteAll(old)) { - error("DatabaseManager", "Failed to unlink old database, stray files! (${old.absolutePath})") - } - oldDatabase.close() - } - } -} diff --git a/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.jvm.kt b/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.jvm.kt new file mode 100644 index 0000000..38c35ce --- /dev/null +++ b/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.jvm.kt @@ -0,0 +1,19 @@ +package moe.lava.banksia.core.room + +import androidx.room.Room +import androidx.room.RoomDatabase +import org.koin.core.parameter.ParametersHolder +import org.koin.core.scope.Scope +import java.io.File + +class JvmDatabaseBuilder() : PlatformDatabaseBuilder { + override fun getBuilder(): RoomDatabase.Builder { + val dbFile = File("./data/room.db") + return Room.databaseBuilder( + name = dbFile.absolutePath, + ) + } +} + +internal actual fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder = + JvmDatabaseBuilder() diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 4150bb2..0883c85 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -5,7 +5,7 @@ plugins { application } -group = "moe.lava.banksia.server" +group = "moe.lava.banksia" version = "1.0.0" application { mainClass.set("moe.lava.banksia.server.ApplicationKt") diff --git a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt index b95b232..e726a46 100644 --- a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt +++ b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt @@ -135,7 +135,7 @@ class GtfsParser( .forEach { fd -> log.info("parsing stop times for ${fd.parent}...") parseStopTimes(fd, trips) { seq -> - seq.chunked(1000000) + seq.chunked(10000) .forEach { emit(GtfsData.StopTimeChunk(it)) } } } @@ -170,7 +170,7 @@ class GtfsParser( id = stop_id, name = stop_name, pos = Point(stop_lat, stop_lon), - parent = parent_station.ifEmpty { null }, + parent = parent_station, hasWheelChairBoarding = wheelchair_boarding == "1", level = level_id, platformCode = platform_code, diff --git a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt index 0981b80..2fe7bf6 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt @@ -28,7 +28,7 @@ import moe.lava.banksia.core.util.serialise import moe.lava.banksia.server.di.ServerModules import moe.lava.banksia.server.gtfsrt.GtfsrtService import org.koin.dsl.module -import org.koin.ktor.ext.get +import org.koin.ktor.ext.inject import org.koin.ktor.plugin.Koin import kotlin.time.Clock @@ -46,29 +46,19 @@ fun Application.module() { modules(ServerModules) } + val gtfsr by inject() @Suppress("KotlinConstantConditions") - launch { get().start(this, !Constants.devMode) } + launch { gtfsr.start(this, !Constants.devMode) } routing { if (Constants.devMode) { get("/fixup") { call.respondText("received") - get().addParentsToStops() + val fixer by inject() + fixer.addParentsToStops() } } - get("/manage/fixup") { - val key = call.parameters["key"] - if (key != Constants.updateKey) { - call.respond(HttpStatusCode.Forbidden) - return@get - } - - call.respondText("fixing") - launch(context = Dispatchers.IO) { - get().addParentsToStops() - } - } - get("/manage/update") { + get("/update") { val key = call.parameters["key"] if (key != Constants.updateKey) { call.respond(HttpStatusCode.Forbidden) @@ -80,13 +70,16 @@ fun Application.module() { ?: "https://opendata.transport.vic.gov.au/dataset/3f4e292e-7f8a-4ffe-831f-1953be0fe448/resource/${datasetUuid}/download/gtfs.zip" call.respondText("received") launch(context = Dispatchers.IO) { - get().import(datasetUrl) - get().addParentsToStops() + val fixer by inject() + val importer by inject() + importer.import(datasetUrl) + + fixer.addParentsToStops() } } get("/metadata/{type?}") { - val dao = get() + val dao by inject() val type = call.parameters["type"] if (type == null) { call.respond(dao.getAll().map { it.asModel() }) @@ -103,7 +96,7 @@ fun Application.module() { get("/routes") { val routes = withContext(context = Dispatchers.IO) { - get().getAll() + inject().value.getAll() } val res = routes.map { it.asModel() } call.respond(res) @@ -111,7 +104,7 @@ fun Application.module() { get("/routes/{route_id}") { val routeId = call.parameters["route_id"]!! val route = withContext(context = Dispatchers.IO) { - get().get(routeId) + inject().value.get(routeId) } if (route != null) call.respond(route.asModel()) @@ -120,7 +113,7 @@ fun Application.module() { } get("/stops") { val routes = withContext(context = Dispatchers.IO) { - get().getAll() + inject().value.getAll() } val res = routes.map { it.asModel() } call.respond(res) @@ -128,7 +121,7 @@ fun Application.module() { get("/stops/{stop_id}") { val stopId = call.parameters["stop_id"]!! val stop = withContext(context = Dispatchers.IO) { - get().get(stopId) + inject().value.get(stopId) } if (stop != null) call.respond(stop.asModel()) @@ -139,7 +132,7 @@ fun Application.module() { val routeId = call.parameters["route_id"]!! val useParent = call.queryParameters["parent"] !in listOf("false", "0") val stops = withContext(Dispatchers.IO) { - val routeDao = get() + val routeDao by inject() if (useParent) routeDao.stopsParent(routeId) else @@ -153,7 +146,7 @@ fun Application.module() { ?.let { LocalDate.parse(it, LocalDate.Formats.ISO) } ?: Clock.System.todayIn(TimeZone.currentSystemDefault()) val times = withContext(context = Dispatchers.IO) { - get() + inject().value .getForStopDated( stopId, listOf(date.dayOfWeek).serialise(), diff --git a/server/src/main/kotlin/moe/lava/banksia/server/GtfsDataFixer.kt b/server/src/main/kotlin/moe/lava/banksia/server/GtfsDataFixer.kt index c74930d..b2620f4 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/GtfsDataFixer.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/GtfsDataFixer.kt @@ -24,14 +24,14 @@ class GtfsDataFixer( name = name, lat = avgLat, lng = avgLng, - parent = null, + parent = "", hasWheelChairBoarding = stops.all { it.hasWheelChairBoarding }, level = "", platformCode = "", ) log("datafixer", "inserting ${parentId} for ${stops.size} children") dao.insertAll(parent) - dao.updateParents(stops.map { it.id }, parentId) + database.stopDao.updateParents(stops.map { it.id }, parentId) } } } diff --git a/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt b/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt index 5c8dc37..a012da7 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt @@ -1,5 +1,7 @@ package moe.lava.banksia.server +import androidx.room.immediateTransaction +import androidx.room.useWriterConnection import io.ktor.util.logging.Logger import moe.lava.banksia.core.model.Route import moe.lava.banksia.core.model.Service @@ -9,7 +11,6 @@ import moe.lava.banksia.core.model.Stop import moe.lava.banksia.core.model.StopTime import moe.lava.banksia.core.model.Trip import moe.lava.banksia.core.room.Database -import moe.lava.banksia.core.room.DatabaseManager import moe.lava.banksia.core.room.entity.asEntity import moe.lava.banksia.server.gtfs.GtfsData import moe.lava.banksia.server.gtfs.GtfsParser @@ -17,66 +18,74 @@ import kotlin.time.Clock class GtfsImporter( private val parser: GtfsParser, - private val dbm: DatabaseManager, + private val database: Database, private val log: Logger, ) { suspend fun import(url: String, date: Long = Clock.System.now().epochSeconds) { - val database = dbm.makeAlt() + database.useWriterConnection { transactor -> + transactor.immediateTransaction { + database.routeDao.deleteAll() + database.serviceDao.deleteAll() + database.serviceExceptionDao.deleteAll() + database.shapeDao.deleteAll() + database.stopDao.deleteAll() + database.stopTimeDao.deleteAll() + database.tripDao.deleteAll() - parser.update(url).collect { chunk -> - when (chunk) { - is GtfsData.RouteChunk -> database.addRoutes(chunk.routes) - is GtfsData.ServiceChunk -> database.addServices(chunk.services) - is GtfsData.ServiceExceptionChunk -> database.addServiceExceptions(chunk.exceptions) - is GtfsData.ShapeChunk -> database.addShapes(chunk.shapes) - is GtfsData.StopChunk -> database.addStops(chunk.stops) - is GtfsData.StopTimeChunk -> database.addStopTimes(chunk.stopTimes) - is GtfsData.TripChunk -> database.addTrips(chunk.trips) + parser.update(url).collect { chunk -> + when (chunk) { + is GtfsData.RouteChunk -> addRoutes(chunk.routes) + is GtfsData.ServiceChunk -> addServices(chunk.services) + is GtfsData.ServiceExceptionChunk -> addServiceExceptions(chunk.exceptions) + is GtfsData.ShapeChunk -> addShapes(chunk.shapes) + is GtfsData.StopChunk -> addStops(chunk.stops) + is GtfsData.StopTimeChunk -> addStopTimes(chunk.stopTimes) + is GtfsData.TripChunk -> addTrips(chunk.trips) + } + } + + updateMetadata(date) } } - - database.updateMetadata(date) - database.close() - dbm.swap() } - private suspend fun Database.updateMetadata(date: Long) { - val dao = versionMetadataDao + private suspend fun updateMetadata(date: Long) { + val dao = database.versionMetadataDao log.info("updating metadata...") dao.update(date, listOf("routes", "stops", "shapes", "trips", "stop_times")) log.info("done") } - private suspend fun Database.addRoutes(routes: List) { - val dao = routeDao + private suspend fun addRoutes(routes: List) { + val dao = database.routeDao log.info("inserting routes...") dao.insertOrReplaceAll(*routes.map { it.asEntity() }.toTypedArray()) log.info("done") } - private suspend fun Database.addServices(services: List) { - val dao = serviceDao + private suspend fun addServices(services: List) { + val dao = database.serviceDao log.info("inserting services...") dao.insertOrReplaceAll(*services.map { it.asEntity() }.toTypedArray()) log.info("done") } - private suspend fun Database.addServiceExceptions(exceptions: List) { - val dao = serviceExceptionDao + private suspend fun addServiceExceptions(exceptions: List) { + val dao = database.serviceExceptionDao log.info("inserting exceptions...") dao.insertOrReplaceAll(*exceptions.map { it.asEntity() }.toTypedArray()) log.info("done") } - private suspend fun Database.addShapes(shapes: List) { - val dao = shapeDao + private suspend fun addShapes(shapes: List) { + val dao = database.shapeDao log.info("inserting shapes...") dao.insertOrReplaceAll(*shapes.map { it.asEntity() }.toTypedArray()) log.info("done") } - private suspend fun Database.addStops(stops: List) { - val dao = stopDao + private suspend fun addStops(stops: List) { + val dao = database.stopDao log.info("inserting stops...") stops .groupBy { it.id } @@ -93,15 +102,15 @@ class GtfsImporter( log.info("done") } - private suspend fun Database.addStopTimes(stopTimes: List) { - val dao = stopTimeDao + private suspend fun addStopTimes(stopTimes: List) { + val dao = database.stopTimeDao log.info("inserting ${stopTimes.size} stoptimes...") dao.insertOrReplaceAll(*stopTimes.map { it.asEntity() }.toTypedArray()) log.info("done") } - private suspend fun Database.addTrips(trips: List) { - val dao = tripDao + private suspend fun addTrips(trips: List) { + val dao = database.tripDao log.info("inserting ${trips.size} trips...") dao.insertOrReplaceAll(*trips.map { it.asEntity() }.toTypedArray()) log.info("done") diff --git a/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt b/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt index a8a7541..7c7fc0b 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt @@ -6,7 +6,6 @@ import moe.lava.banksia.server.GtfsDataFixer import moe.lava.banksia.server.GtfsImporter import moe.lava.banksia.server.gtfs.GtfsParser import moe.lava.banksia.server.gtfsrt.GtfsrtService -import org.koin.core.module.dsl.factoryOf import org.koin.core.module.dsl.singleOf import org.koin.dsl.module @@ -17,6 +16,6 @@ val ServerModules = module { singleOf(::GtfsParser) singleOf(::GtfsrtService) - factoryOf(::GtfsDataFixer) - factoryOf(::GtfsImporter) + singleOf(::GtfsDataFixer) + singleOf(::GtfsImporter) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 9abcf7f..335422d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,8 +37,6 @@ include(":server:gtfs") include(":server:gtfs_rt") include(":core") include(":core:data") -include(":core:data:client") -include(":core:data:server") include(":core:room") include(":ui") include(":ui:maps") diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index 9c5c7bd..018c8e6 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -68,7 +68,7 @@ kotlin { implementation(libs.ui.backhandler) implementation(projects.core) - implementation(projects.core.data.client) + implementation(projects.core.data) implementation(projects.ui.maps) implementation(projects.ui.shared) } diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/di/AppModule.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/di/AppModule.kt index a2b4d7e..cff36fb 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/di/AppModule.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/di/AppModule.kt @@ -1,12 +1,12 @@ package moe.lava.banksia.ui.di -import moe.lava.banksia.core.data.clientDataDiModule +import moe.lava.banksia.core.data.dataDiModule import moe.lava.banksia.ui.screens.map.MapScreenViewModel import org.koin.core.module.dsl.viewModelOf import org.koin.dsl.module val AppModule = module { - includes(clientDataDiModule) + includes(dataDiModule) // ViewModel viewModelOf(::MapScreenViewModel)