Compare commits
No commits in common. "master" and "refactor/huge" have entirely different histories.
master
...
refactor/h
177 changed files with 4896 additions and 2635 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -18,6 +18,6 @@ captures
|
||||||
**/xcshareddata/WorkspaceSettings.xcsettings
|
**/xcshareddata/WorkspaceSettings.xcsettings
|
||||||
|
|
||||||
secrets.properties
|
secrets.properties
|
||||||
/core/src/commonMain/kotlin/moe/lava/banksia/core/Constants.kt
|
shared/src/commonMain/kotlin/moe/lava/banksia/Constants.kt
|
||||||
/data/
|
/data/
|
||||||
/data
|
/data
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ plugins {
|
||||||
alias(libs.plugins.composeCompiler) apply false
|
alias(libs.plugins.composeCompiler) apply false
|
||||||
alias(libs.plugins.kotlinJvm) apply false
|
alias(libs.plugins.kotlinJvm) apply false
|
||||||
alias(libs.plugins.kotlinMultiplatform) apply false
|
alias(libs.plugins.kotlinMultiplatform) apply false
|
||||||
alias(libs.plugins.sqldelight) apply false
|
|
||||||
alias(libs.plugins.wire) apply false
|
alias(libs.plugins.wire) apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,11 @@ plugins {
|
||||||
alias(libs.plugins.kotlinMultiplatform)
|
alias(libs.plugins.kotlinMultiplatform)
|
||||||
alias(libs.plugins.kotlinSerialization)
|
alias(libs.plugins.kotlinSerialization)
|
||||||
alias(libs.plugins.androidMultiplatformLibrary)
|
alias(libs.plugins.androidMultiplatformLibrary)
|
||||||
alias(libs.plugins.sqldelight)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
android {
|
android {
|
||||||
namespace = "moe.lava.banksia.core.sqld"
|
namespace = "moe.lava.banksia.client"
|
||||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||||
|
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
|
|
@ -17,37 +16,28 @@ kotlin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compilerOptions {
|
||||||
|
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
|
||||||
|
}
|
||||||
|
|
||||||
iosArm64()
|
iosArm64()
|
||||||
iosSimulatorArm64()
|
iosSimulatorArm64()
|
||||||
|
|
||||||
jvm()
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
androidMain.dependencies {
|
androidMain.dependencies {
|
||||||
implementation(libs.sqldelight.driver.android)
|
implementation(libs.compose.ui.tooling.preview)
|
||||||
|
implementation(libs.androidx.activity.compose)
|
||||||
|
implementation(libs.kotlinx.coroutines.android)
|
||||||
|
implementation(libs.play.services.location)
|
||||||
}
|
}
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
implementation(libs.okio)
|
|
||||||
implementation(libs.koin.core)
|
implementation(libs.koin.core)
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
implementation(libs.kotlinx.datetime)
|
implementation(libs.kotlinx.datetime)
|
||||||
|
implementation(libs.ktor.client.core)
|
||||||
implementation(projects.core)
|
implementation(libs.ktor.client.contentnegotiation)
|
||||||
}
|
implementation(libs.ktor.serialization.kotlinx.json)
|
||||||
nativeMain.dependencies {
|
implementation(projects.shared)
|
||||||
implementation(libs.sqldelight.driver.native)
|
|
||||||
}
|
|
||||||
jvmMain.dependencies {
|
|
||||||
implementation(libs.sqldelight.driver.jvm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sqldelight {
|
|
||||||
databases {
|
|
||||||
register("BanksiaDatabase") {
|
|
||||||
packageName.set("moe.lava.banksia.core.sqld")
|
|
||||||
schemaOutputDirectory.set(file("src/commonMain/sqldelight/schema"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package moe.lava.banksia.client.data.route
|
||||||
|
|
||||||
|
import moe.lava.banksia.model.Route
|
||||||
|
import moe.lava.banksia.room.dao.RouteDao
|
||||||
|
import moe.lava.banksia.room.entity.asEntity
|
||||||
|
|
||||||
|
class RouteLocalDataSource(private val dao: RouteDao) {
|
||||||
|
suspend fun get(id: String) = dao.get(id)
|
||||||
|
suspend fun getAll() = dao.getAll()
|
||||||
|
suspend fun save(vararg routes: Route) = dao.insertOrReplaceAll(*routes.map { it.asEntity() }.toTypedArray())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package moe.lava.banksia.client.data.route
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import moe.lava.banksia.model.Route
|
||||||
|
|
||||||
|
class RouteRemoteDataSource(val client: HttpClient) {
|
||||||
|
suspend fun get(id: String) = client.get("routes/${id}").body<Route>()
|
||||||
|
suspend fun getAll() = client.get("routes").body<List<Route>>()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package moe.lava.banksia.client.data.stop
|
||||||
|
|
||||||
|
import moe.lava.banksia.model.Stop
|
||||||
|
import moe.lava.banksia.room.dao.RouteDao
|
||||||
|
import moe.lava.banksia.room.dao.StopDao
|
||||||
|
import moe.lava.banksia.room.entity.asEntity
|
||||||
|
|
||||||
|
class StopLocalDataSource(private val dao: StopDao, private val routeDao: RouteDao) {
|
||||||
|
suspend fun get(id: String) = dao.get(id)
|
||||||
|
suspend fun getByRoute(id: String) = routeDao.stops(id)
|
||||||
|
suspend fun save(vararg stops: Stop) = dao.insertOrReplaceAll(*stops.map { it.asEntity() }.toTypedArray())
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package moe.lava.banksia.core.data.sources.stop
|
package moe.lava.banksia.client.data.stop
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
import moe.lava.banksia.core.model.Stop
|
import moe.lava.banksia.model.Stop
|
||||||
|
|
||||||
internal class StopRemoteDataSource(val client: HttpClient) {
|
class StopRemoteDataSource(val client: HttpClient) {
|
||||||
suspend fun get(id: String) = client.get("stops/${id}").body<Stop>()
|
suspend fun get(id: String) = client.get("stops/${id}").body<Stop>()
|
||||||
suspend fun getByRoute(id: String) = client.get("route_stops/${id}").body<List<Stop>>()
|
suspend fun getByRoute(id: String) = client.get("route_stops/${id}").body<List<Stop>>()
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package moe.lava.banksia.client.data.stoptime
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
import kotlinx.datetime.todayIn
|
||||||
|
import moe.lava.banksia.model.StopTimeDated
|
||||||
|
import moe.lava.banksia.model.atDate
|
||||||
|
import moe.lava.banksia.room.dao.StopTimeDao
|
||||||
|
import moe.lava.banksia.util.serialise
|
||||||
|
import kotlin.time.Clock
|
||||||
|
|
||||||
|
class StopTimeLocalDataSource(
|
||||||
|
private val stopTimeDao: StopTimeDao,
|
||||||
|
) {
|
||||||
|
suspend fun getAtStop(
|
||||||
|
stopId: String,
|
||||||
|
date: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault()),
|
||||||
|
): List<StopTimeDated> {
|
||||||
|
return stopTimeDao
|
||||||
|
.getForStopDated(
|
||||||
|
stopId,
|
||||||
|
listOf(date.dayOfWeek).serialise(),
|
||||||
|
date.toEpochDays().toInt(),
|
||||||
|
)
|
||||||
|
.map { it.asModel().atDate(date) }
|
||||||
|
.sortedBy { it.departureTime }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package moe.lava.banksia.client.data.stoptime
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.parameter
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
import kotlinx.datetime.todayIn
|
||||||
|
import moe.lava.banksia.model.StopTimeDated
|
||||||
|
import kotlin.time.Clock
|
||||||
|
|
||||||
|
class StopTimeRemoteDataSource(
|
||||||
|
private val client: HttpClient,
|
||||||
|
) {
|
||||||
|
suspend fun getAtStop(
|
||||||
|
stopId: String,
|
||||||
|
date: LocalDate? = Clock.System.todayIn(TimeZone.currentSystemDefault()),
|
||||||
|
): List<StopTimeDated> {
|
||||||
|
return client.get("stoptimes/by_stop/${stopId}") {
|
||||||
|
parameter("date", date)
|
||||||
|
}.body<List<StopTimeDated>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*suspend fun get(
|
||||||
|
stop: String? = null,
|
||||||
|
trip: String? = null,
|
||||||
|
day: DayOfWeek? = Clock.System.todayIn(TimeZone.currentSystemDefault()).dayOfWeek,
|
||||||
|
): List<StopTime> {
|
||||||
|
return client.get("stoptimes") {
|
||||||
|
stop?.let { parameter("stop", it) }
|
||||||
|
trip?.let { parameter("trip", it) }
|
||||||
|
day?.let { parameter("day", it) }
|
||||||
|
}.body<List<StopTime>>()
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package moe.lava.banksia.client.data.trip
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import kotlinx.datetime.DayOfWeek
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
import kotlinx.datetime.todayIn
|
||||||
|
import moe.lava.banksia.model.Trip
|
||||||
|
import kotlin.time.Clock
|
||||||
|
|
||||||
|
class TripRemoteDataSource(
|
||||||
|
private val client: HttpClient,
|
||||||
|
) {
|
||||||
|
suspend fun get(
|
||||||
|
day: DayOfWeek? = Clock.System.todayIn(TimeZone.currentSystemDefault()).dayOfWeek,
|
||||||
|
): List<Trip> {
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package moe.lava.banksia.core.data
|
package moe.lava.banksia.client.di
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.plugins.HttpSend
|
import io.ktor.client.plugins.HttpSend
|
||||||
|
|
@ -7,22 +7,22 @@ import io.ktor.client.plugins.defaultRequest
|
||||||
import io.ktor.client.plugins.plugin
|
import io.ktor.client.plugins.plugin
|
||||||
import io.ktor.serialization.kotlinx.json.json
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import moe.lava.banksia.core.Constants
|
import moe.lava.banksia.Constants
|
||||||
import moe.lava.banksia.core.data.repositories.ClientRouteRepository
|
import moe.lava.banksia.client.data.route.RouteLocalDataSource
|
||||||
import moe.lava.banksia.core.data.repositories.ClientStopRepository
|
import moe.lava.banksia.client.data.route.RouteRemoteDataSource
|
||||||
import moe.lava.banksia.core.data.repositories.RouteRepository
|
import moe.lava.banksia.client.data.stop.StopLocalDataSource
|
||||||
import moe.lava.banksia.core.data.repositories.StopRepository
|
import moe.lava.banksia.client.data.stop.StopRemoteDataSource
|
||||||
import moe.lava.banksia.core.data.sources.route.RouteLocalDataSource
|
import moe.lava.banksia.client.data.stoptime.StopTimeLocalDataSource
|
||||||
import moe.lava.banksia.core.data.sources.route.RouteRemoteDataSource
|
import moe.lava.banksia.client.data.stoptime.StopTimeRemoteDataSource
|
||||||
import moe.lava.banksia.core.data.sources.stop.StopLocalDataSource
|
import moe.lava.banksia.client.repository.RouteRepository
|
||||||
import moe.lava.banksia.core.data.sources.stop.StopRemoteDataSource
|
import moe.lava.banksia.client.repository.StopRepository
|
||||||
import moe.lava.banksia.core.util.log
|
import moe.lava.banksia.client.repository.StopTimeRepository
|
||||||
import moe.lava.banksia.data.ptv.PtvService
|
import moe.lava.banksia.data.ptv.PtvService
|
||||||
|
import moe.lava.banksia.util.log
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
import org.koin.dsl.bind
|
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
actual val platformModule = module {
|
val ClientModule = module {
|
||||||
// HTTP Clients
|
// HTTP Clients
|
||||||
singleOf(::PtvService)
|
singleOf(::PtvService)
|
||||||
single {
|
single {
|
||||||
|
|
@ -49,8 +49,11 @@ actual val platformModule = module {
|
||||||
singleOf(::RouteRemoteDataSource)
|
singleOf(::RouteRemoteDataSource)
|
||||||
singleOf(::StopLocalDataSource)
|
singleOf(::StopLocalDataSource)
|
||||||
singleOf(::StopRemoteDataSource)
|
singleOf(::StopRemoteDataSource)
|
||||||
|
singleOf(::StopTimeLocalDataSource)
|
||||||
|
singleOf(::StopTimeRemoteDataSource)
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
singleOf(::ClientRouteRepository) bind RouteRepository::class
|
singleOf(::RouteRepository)
|
||||||
singleOf(::ClientStopRepository) bind StopRepository::class
|
singleOf(::StopRepository)
|
||||||
|
singleOf(::StopTimeRepository)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package moe.lava.banksia.client.repository
|
||||||
|
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import moe.lava.banksia.client.data.route.RouteLocalDataSource
|
||||||
|
import moe.lava.banksia.client.data.route.RouteRemoteDataSource
|
||||||
|
|
||||||
|
class RouteRepository(
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package moe.lava.banksia.client.repository
|
||||||
|
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import moe.lava.banksia.client.data.stop.StopLocalDataSource
|
||||||
|
import moe.lava.banksia.client.data.stop.StopRemoteDataSource
|
||||||
|
|
||||||
|
class StopRepository(
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package moe.lava.banksia.client.repository
|
||||||
|
|
||||||
|
import moe.lava.banksia.client.data.stoptime.StopTimeLocalDataSource
|
||||||
|
import moe.lava.banksia.client.data.stoptime.StopTimeRemoteDataSource
|
||||||
|
import moe.lava.banksia.model.StopTimeDated
|
||||||
|
|
||||||
|
class StopTimeRepository(
|
||||||
|
private val local: StopTimeLocalDataSource,
|
||||||
|
private val remote: StopTimeRemoteDataSource,
|
||||||
|
) {
|
||||||
|
suspend fun getForStop(id: String): List<StopTimeDated> {
|
||||||
|
return local
|
||||||
|
.getAtStop(id)
|
||||||
|
.ifEmpty { remote.getAtStop(id) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
alias(libs.plugins.kotlinMultiplatform)
|
|
||||||
alias(libs.plugins.androidMultiplatformLibrary)
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
android {
|
|
||||||
namespace = "moe.lava.banksia.core.data"
|
|
||||||
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 {
|
|
||||||
val clientMain by creating {
|
|
||||||
dependsOn(commonMain.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
androidMain.get().dependsOn(clientMain)
|
|
||||||
iosArm64Main.get().dependsOn(clientMain)
|
|
||||||
iosSimulatorArm64Main.get().dependsOn(clientMain)
|
|
||||||
|
|
||||||
commonMain.dependencies {
|
|
||||||
implementation(libs.koin.core)
|
|
||||||
implementation(projects.core)
|
|
||||||
api(projects.core.stoptime)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.sqld)
|
|
||||||
}
|
|
||||||
iosMain.dependencies {
|
|
||||||
implementation(libs.ktor.client.darwin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +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
|
|
||||||
import moe.lava.banksia.core.model.Route
|
|
||||||
import moe.lava.banksia.core.sqld.mappers.asModel
|
|
||||||
|
|
||||||
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()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val tripRouteMap = mutableMapOf<Long, Route>()
|
|
||||||
|
|
||||||
override suspend fun get(id: String) = mutex.withLock { local.get(id)?.asModel() ?: remote.get(id) }
|
|
||||||
override suspend fun getByPattern(patternId: Long) = mutex.withLock {
|
|
||||||
tripRouteMap[patternId]
|
|
||||||
?: remote.getByPattern(patternId).also {
|
|
||||||
local.save(it)
|
|
||||||
tripRouteMap[patternId] = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +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
|
|
||||||
import moe.lava.banksia.core.sqld.mappers.asModel
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.sources.route
|
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.IO
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import moe.lava.banksia.core.model.Route
|
|
||||||
import moe.lava.banksia.core.sqld.RouteQueries
|
|
||||||
import moe.lava.banksia.core.sqld.mappers.asDb
|
|
||||||
|
|
||||||
internal class RouteLocalDataSource(private val queries: RouteQueries) {
|
|
||||||
suspend fun get(id: String) = withContext(Dispatchers.IO) { queries.get(id).executeAsOneOrNull() }
|
|
||||||
suspend fun getAll() = withContext(Dispatchers.IO) { queries.getAll().executeAsList() }
|
|
||||||
// suspend fun getByTrip(tripId: String) = dao.getByTrip(tripId)
|
|
||||||
suspend fun save(vararg routes: Route) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
queries.transaction {
|
|
||||||
routes.forEach {
|
|
||||||
queries.insert(it.asDb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.sources.route
|
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import moe.lava.banksia.core.model.Route
|
|
||||||
|
|
||||||
internal class RouteRemoteDataSource(val client: HttpClient) {
|
|
||||||
suspend fun get(id: String) = client.get("routes/${id}").body<Route>()
|
|
||||||
suspend fun getByPattern(patternId: Long) = client.get("routes/by_pattern/${patternId}").body<Route>()
|
|
||||||
suspend fun getAll() = client.get("routes").body<List<Route>>()
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.sources.stop
|
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.IO
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import moe.lava.banksia.core.model.Stop
|
|
||||||
import moe.lava.banksia.core.sqld.StopQueries
|
|
||||||
import moe.lava.banksia.core.sqld.mappers.asDb
|
|
||||||
|
|
||||||
internal class StopLocalDataSource(private val queries: StopQueries) {
|
|
||||||
suspend fun get(id: String) = withContext(Dispatchers.IO) { queries.get(id).executeAsOneOrNull() }
|
|
||||||
suspend fun getByRoute(id: String) = withContext(Dispatchers.IO) { queries.getByRoute(id).executeAsList() }
|
|
||||||
suspend fun save(vararg stops: Stop) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
queries.transaction {
|
|
||||||
stops.forEach {
|
|
||||||
queries.insert(it.asDb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
package moe.lava.banksia.core.data
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.sqld.sqldDiModule
|
|
||||||
import org.koin.core.module.Module
|
|
||||||
import org.koin.dsl.module
|
|
||||||
|
|
||||||
internal expect val platformModule: Module
|
|
||||||
|
|
||||||
val dataDiModule = module {
|
|
||||||
includes(platformModule)
|
|
||||||
includes(sqldDiModule)
|
|
||||||
includes(stopTimeDataDiModule)
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.repositories
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.model.Route
|
|
||||||
|
|
||||||
interface RouteRepository {
|
|
||||||
suspend fun get(id: String): Route?
|
|
||||||
suspend fun getByPattern(patternId: Long): Route?
|
|
||||||
suspend fun getAll(): List<Route>
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.repositories
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.model.Stop
|
|
||||||
|
|
||||||
interface StopRepository {
|
|
||||||
suspend fun get(id: String): Stop
|
|
||||||
suspend fun getByRoute(id: String): List<Stop>
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package moe.lava.banksia.core.data
|
|
||||||
|
|
||||||
import org.koin.dsl.module
|
|
||||||
|
|
||||||
internal actual val platformModule = module {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
|
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
import org.koin.core.component.get
|
|
||||||
|
|
||||||
actual class DatabaseManager : KoinComponent {
|
|
||||||
actual val database by lazy {
|
|
||||||
val ctx = get<Context>().applicationContext
|
|
||||||
val driver = AndroidSqliteDriver(BanksiaDatabase.Schema, ctx, "${DBNAME}.db")
|
|
||||||
BanksiaDatabase(driver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld
|
|
||||||
|
|
||||||
internal const val DBNAME = "timetable"
|
|
||||||
|
|
||||||
expect class DatabaseManager() {
|
|
||||||
val database: BanksiaDatabase
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld
|
|
||||||
|
|
||||||
import org.koin.core.module.dsl.singleOf
|
|
||||||
import org.koin.dsl.module
|
|
||||||
|
|
||||||
val sqldDiModule = module {
|
|
||||||
singleOf(::DatabaseManager)
|
|
||||||
factory { get<DatabaseManager>().database }
|
|
||||||
factory { get<BanksiaDatabase>().routeQueries }
|
|
||||||
factory { get<BanksiaDatabase>().serviceQueries }
|
|
||||||
factory { get<BanksiaDatabase>().serviceExceptionQueries }
|
|
||||||
factory { get<BanksiaDatabase>().shapeQueries }
|
|
||||||
factory { get<BanksiaDatabase>().stopQueries }
|
|
||||||
factory { get<BanksiaDatabase>().stoppingPatternQueries }
|
|
||||||
factory { get<BanksiaDatabase>().stopTimeQueries }
|
|
||||||
factory { get<BanksiaDatabase>().tripQueries }
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld.mappers
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.model.Route
|
|
||||||
import moe.lava.banksia.core.model.RouteType
|
|
||||||
import moe.lava.banksia.core.sqld.Route as DbRoute
|
|
||||||
|
|
||||||
fun DbRoute.asModel() = Route(
|
|
||||||
id = id,
|
|
||||||
type = RouteType.from(type.toInt()),
|
|
||||||
number = number,
|
|
||||||
name = name,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Route.asDb() = DbRoute(id, type.value.toLong(), number, name)
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld.mappers
|
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import moe.lava.banksia.core.model.Service
|
|
||||||
import moe.lava.banksia.core.util.deserialiseDaysBitflag
|
|
||||||
import moe.lava.banksia.core.util.serialise
|
|
||||||
import moe.lava.banksia.core.sqld.Service as DbService
|
|
||||||
|
|
||||||
fun DbService.asModel() = Service(
|
|
||||||
id = id,
|
|
||||||
days = days.toInt().deserialiseDaysBitflag(),
|
|
||||||
start = LocalDate.fromEpochDays(start),
|
|
||||||
end = LocalDate.fromEpochDays(end),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Service.asDb() = DbService(
|
|
||||||
id = id,
|
|
||||||
days = days.serialise().toLong(),
|
|
||||||
start = start.toEpochDays(),
|
|
||||||
end = end.toEpochDays(),
|
|
||||||
)
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld.mappers
|
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import moe.lava.banksia.core.model.ServiceException
|
|
||||||
import moe.lava.banksia.core.sqld.ServiceException as DbServiceException
|
|
||||||
|
|
||||||
fun DbServiceException.asModel() = ServiceException(
|
|
||||||
serviceId = serviceId,
|
|
||||||
date = LocalDate.fromEpochDays(date),
|
|
||||||
type = type.toInt(),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun ServiceException.asDb() = DbServiceException(
|
|
||||||
serviceId = serviceId,
|
|
||||||
type = date.toEpochDays(),
|
|
||||||
date = type.toLong(),
|
|
||||||
)
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld.mappers
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.model.Shape
|
|
||||||
import moe.lava.banksia.core.model.ShapePath
|
|
||||||
import moe.lava.banksia.core.util.Point
|
|
||||||
import moe.lava.banksia.core.sqld.Shape as DbShape
|
|
||||||
|
|
||||||
fun DbShape.asModel() = Shape(
|
|
||||||
id = id,
|
|
||||||
path = bytesToPath(path),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Shape.asDb() = DbShape(
|
|
||||||
id = id,
|
|
||||||
path = bytesFromPath(path),
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun bytesToPath(value: ByteArray): ShapePath {
|
|
||||||
return value
|
|
||||||
.asSequence()
|
|
||||||
.asIterable()
|
|
||||||
.chunked(8) {
|
|
||||||
(it[0].toLong() and 0xFF) or
|
|
||||||
(it[1].toLong() and 0xFF shl 8) or
|
|
||||||
(it[2].toLong() and 0xFF shl 16) or
|
|
||||||
(it[3].toLong() and 0xFF shl 24) or
|
|
||||||
(it[4].toLong() and 0xFF shl 32) or
|
|
||||||
(it[5].toLong() and 0xFF shl 40) or
|
|
||||||
(it[6].toLong() and 0xFF shl 48) or
|
|
||||||
(it[7].toLong() and 0xFF shl 56)
|
|
||||||
}
|
|
||||||
.map { Double.fromBits(it) }
|
|
||||||
.chunked(2)
|
|
||||||
.map { (lat, lng) -> Point(lat, lng) }
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bytesFromPath(path: ShapePath): ByteArray {
|
|
||||||
return path
|
|
||||||
.flatMap { (lat, lng) -> listOf(lat.toBits(), lng.toBits()) }
|
|
||||||
.flatMap { i -> listOf(
|
|
||||||
i.toByte(),
|
|
||||||
(i shr 8).toByte(),
|
|
||||||
(i shr 16).toByte(),
|
|
||||||
(i shr 24).toByte(),
|
|
||||||
(i shr 32).toByte(),
|
|
||||||
(i shr 40).toByte(),
|
|
||||||
(i shr 48).toByte(),
|
|
||||||
(i shr 56).toByte(),
|
|
||||||
) }
|
|
||||||
.toByteArray()
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld.mappers
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.model.Stop
|
|
||||||
import moe.lava.banksia.core.util.Point
|
|
||||||
import moe.lava.banksia.core.sqld.Stop as DbStop
|
|
||||||
|
|
||||||
fun DbStop.asModel() = Stop(
|
|
||||||
id = id,
|
|
||||||
name = name,
|
|
||||||
pos = Point(lat, lng),
|
|
||||||
parent = parent,
|
|
||||||
hasWheelChairBoarding = hasWheelChairBoarding == 1L,
|
|
||||||
level = level,
|
|
||||||
platformCode = platformCode,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Stop.asDb() = DbStop(
|
|
||||||
id = id,
|
|
||||||
name = name,
|
|
||||||
lat = pos.lat,
|
|
||||||
lng = pos.lng,
|
|
||||||
parent = parent,
|
|
||||||
hasWheelChairBoarding = if (hasWheelChairBoarding) 1L else 0L,
|
|
||||||
level = level,
|
|
||||||
platformCode = platformCode
|
|
||||||
)
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld.mappers
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.model.FutureTime
|
|
||||||
import moe.lava.banksia.core.model.FutureTime.Companion.asInt
|
|
||||||
import moe.lava.banksia.core.model.StopTime
|
|
||||||
import moe.lava.banksia.core.model.TimeType
|
|
||||||
import moe.lava.banksia.core.sqld.StopTime as DbStopTime
|
|
||||||
|
|
||||||
fun DbStopTime.asModel() = StopTime(
|
|
||||||
patternId = patternId,
|
|
||||||
stopId = stopId,
|
|
||||||
time = TimeType.Undated(
|
|
||||||
arrival = FutureTime.fromInt((departureTime + arrivalDelta).toInt()),
|
|
||||||
departure = FutureTime.fromInt(departureTime.toInt()),
|
|
||||||
),
|
|
||||||
pickupType = pickupType.toInt(),
|
|
||||||
dropOffType = dropOffType.toInt(),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun StopTime.Undated.asDb() = DbStopTime(
|
|
||||||
patternId = patternId,
|
|
||||||
stopId = stopId,
|
|
||||||
arrivalDelta = (time.arrival.asInt() - time.departure.asInt()).toLong(),
|
|
||||||
departureTime = time.departure.asInt().toLong(),
|
|
||||||
pickupType = pickupType.toLong(),
|
|
||||||
dropOffType = dropOffType.toLong(),
|
|
||||||
)
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld.mappers
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.model.StopTime
|
|
||||||
import moe.lava.banksia.core.model.StoppingPattern
|
|
||||||
import moe.lava.banksia.core.model.TimeType
|
|
||||||
import moe.lava.banksia.core.sqld.StoppingPattern as DbStoppingPattern
|
|
||||||
|
|
||||||
fun <T: TimeType> DbStoppingPattern.asModel(stoptimes: List<StopTime<T>>) = StoppingPattern(
|
|
||||||
id = id,
|
|
||||||
routeId = routeId,
|
|
||||||
shapeId = shapeId,
|
|
||||||
headsign = headsign,
|
|
||||||
wheelchairAccessible = wheelchairAccessible == 1L,
|
|
||||||
stoptimes = stoptimes,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun StoppingPattern<*>.asDb() = DbStoppingPattern(
|
|
||||||
id = id,
|
|
||||||
routeId = routeId,
|
|
||||||
shapeId = shapeId,
|
|
||||||
headsign = headsign,
|
|
||||||
wheelchairAccessible = if (wheelchairAccessible) 1L else 0L,
|
|
||||||
)
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld.mappers
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.model.Service
|
|
||||||
import moe.lava.banksia.core.model.StoppingPattern
|
|
||||||
import moe.lava.banksia.core.model.Trip
|
|
||||||
import moe.lava.banksia.core.sqld.Trip as DbTrip
|
|
||||||
|
|
||||||
fun DbTrip.asModel(pattern: StoppingPattern.Undated, service: Service): Trip.Undated {
|
|
||||||
if (serviceId != service.id) {
|
|
||||||
throw IllegalArgumentException("trip and service id mismatch (${serviceId} != ${service.id})")
|
|
||||||
}
|
|
||||||
return Trip(
|
|
||||||
id = gtfsId,
|
|
||||||
pattern = pattern,
|
|
||||||
service = service,
|
|
||||||
directionId = directionId.toInt(),
|
|
||||||
blockId = blockId.toString(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Trip.Undated.asDb() = DbTrip(
|
|
||||||
gtfsId = id,
|
|
||||||
patternId = pattern.id,
|
|
||||||
serviceId = service.id,
|
|
||||||
directionId = directionId.toLong(),
|
|
||||||
blockId = blockId?.toLong(),
|
|
||||||
)
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
CREATE TABLE Route (
|
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
|
||||||
type INTEGER NOT NULL,
|
|
||||||
number TEXT,
|
|
||||||
name TEXT NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
getAll:
|
|
||||||
SELECT * FROM Route;
|
|
||||||
|
|
||||||
get:
|
|
||||||
SELECT * FROM Route WHERE id == ?;
|
|
||||||
|
|
||||||
getByPattern:
|
|
||||||
SELECT Route.* FROM Route
|
|
||||||
INNER JOIN StoppingPattern ON Route.id == StoppingPattern.routeId
|
|
||||||
WHERE StoppingPattern.id == :patternId;
|
|
||||||
|
|
||||||
insert:
|
|
||||||
INSERT OR REPLACE INTO Route VALUES ?;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
CREATE TABLE Service (
|
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
|
||||||
days INTEGER NOT NULL,
|
|
||||||
start INTEGER NOT NULL,
|
|
||||||
end INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_Service_days ON Service (days);
|
|
||||||
|
|
||||||
insert:
|
|
||||||
INSERT INTO Service VALUES ?;
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
CREATE TABLE ServiceException (
|
|
||||||
serviceId TEXT NOT NULL,
|
|
||||||
type INTEGER NOT NULL,
|
|
||||||
date INTEGER NOT NULL,
|
|
||||||
PRIMARY KEY (serviceId, type)
|
|
||||||
);
|
|
||||||
|
|
||||||
insert:
|
|
||||||
INSERT INTO ServiceException VALUES ?;
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
CREATE TABLE Shape (
|
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
|
||||||
path BLOB NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
insert:
|
|
||||||
INSERT INTO Shape VALUES ?;
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
CREATE TABLE Stop (
|
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
lat REAL NOT NULL,
|
|
||||||
lng REAL NOT NULL,
|
|
||||||
parent TEXT REFERENCES Stop(id),
|
|
||||||
hasWheelChairBoarding INTEGER NOT NULL,
|
|
||||||
level TEXT,
|
|
||||||
platformCode TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_Stop_parent ON Stop (parent);
|
|
||||||
|
|
||||||
getAll:
|
|
||||||
SELECT * FROM Stop;
|
|
||||||
|
|
||||||
getAllParentless:
|
|
||||||
SELECT * FROM Stop WHERE platformCode IS NOT NULL AND parent IS NULL;
|
|
||||||
|
|
||||||
get:
|
|
||||||
SELECT * FROM Stop WHERE id == ?;
|
|
||||||
|
|
||||||
getMany:
|
|
||||||
SELECT * FROM Stop WHERE id IN ?;
|
|
||||||
|
|
||||||
insert:
|
|
||||||
INSERT INTO Stop VALUES ?;
|
|
||||||
|
|
||||||
updateParents:
|
|
||||||
UPDATE Stop SET parent = ? WHERE id IN ?;
|
|
||||||
|
|
||||||
getByRoute:
|
|
||||||
SELECT Stop.* FROM Stop
|
|
||||||
INNER JOIN StopTime ON StopTime.stopId == Stop.id
|
|
||||||
INNER JOIN StoppingPattern ON StoppingPattern.id == StopTime.patternId
|
|
||||||
WHERE StoppingPattern.routeId == :id
|
|
||||||
GROUP BY Stop.id;
|
|
||||||
|
|
||||||
-- I vibecoded this, sorry
|
|
||||||
getParentsByRoute:
|
|
||||||
WITH RECURSIVE Tree AS (
|
|
||||||
SELECT Stop.* FROM Stop
|
|
||||||
INNER JOIN StopTime ON StopTime.stopId == Stop.id
|
|
||||||
INNER JOIN StoppingPattern ON StoppingPattern.id == StopTime.patternId
|
|
||||||
WHERE StoppingPattern.routeId == :id
|
|
||||||
GROUP BY Stop.id
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT s.*
|
|
||||||
FROM Stop s
|
|
||||||
INNER JOIN Tree t ON s.id = t.parent
|
|
||||||
)
|
|
||||||
SELECT DISTINCT * FROM Tree WHERE parent IS NULL;
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
CREATE TABLE StopTime (
|
|
||||||
patternId INTEGER NOT NULL REFERENCES StoppingPattern (id),
|
|
||||||
stopId TEXT NOT NULL REFERENCES Stop (id),
|
|
||||||
arrivalDelta INTEGER NOT NULL,
|
|
||||||
departureTime INTEGER NOT NULL,
|
|
||||||
pickupType INTEGER NOT NULL,
|
|
||||||
dropOffType INTEGER NOT NULL,
|
|
||||||
PRIMARY KEY (patternId, stopId)
|
|
||||||
) WITHOUT ROWID;
|
|
||||||
|
|
||||||
CREATE INDEX idx_StopTime_stopId ON StopTime (stopId);
|
|
||||||
|
|
||||||
insert:
|
|
||||||
INSERT OR REPLACE INTO StopTime VALUES ?;
|
|
||||||
|
|
||||||
getForStopDated:
|
|
||||||
SELECT DISTINCT StopTime.* FROM StopTime
|
|
||||||
INNER JOIN Service ON Service.days & :days = :days AND :date BETWEEN Service.start AND Service.`end`
|
|
||||||
LEFT JOIN ServiceException ON ServiceException.serviceId == Service.id AND ServiceException.date == :date
|
|
||||||
INNER JOIN Trip ON Trip.serviceId == Service.id
|
|
||||||
INNER JOIN StoppingPattern ON StoppingPattern.id == Trip.patternId
|
|
||||||
WHERE StopTime.patternId == StoppingPattern.id
|
|
||||||
AND StopTime.stopId IN (SELECT Stop.id FROM Stop WHERE Stop.parent == :stopId OR Stop.id == :stopId)
|
|
||||||
AND ServiceException.type IS NULL;
|
|
||||||
|
|
||||||
getExtendedForStop:
|
|
||||||
SELECT DISTINCT
|
|
||||||
StopTime.patternId,
|
|
||||||
StopTime.arrivalDelta,
|
|
||||||
StopTime.departureTime,
|
|
||||||
StoppingPattern.headsign,
|
|
||||||
Route.type AS routeType,
|
|
||||||
Route.number AS routeNumber,
|
|
||||||
Route.name AS routeName,
|
|
||||||
Stop.platformCode AS stopPlatformCode
|
|
||||||
FROM StopTime
|
|
||||||
INNER JOIN Service ON Service.days & :days = :days AND :date BETWEEN Service.start AND Service.`end`
|
|
||||||
LEFT JOIN ServiceException ON ServiceException.serviceId == Service.id AND ServiceException.date == :date
|
|
||||||
INNER JOIN Trip ON Trip.serviceId == Service.id
|
|
||||||
INNER JOIN StoppingPattern ON StoppingPattern.id == Trip.patternId
|
|
||||||
INNER JOIN Route ON Route.id == StoppingPattern.routeId
|
|
||||||
INNER JOIN Stop ON Stop.id == StopTime.stopId
|
|
||||||
WHERE StopTime.patternId == StoppingPattern.id
|
|
||||||
AND StopTime.stopId IN (SELECT Stop.id FROM Stop WHERE Stop.parent == :stopId OR Stop.id == :stopId)
|
|
||||||
AND ServiceException.type IS NULL;
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
CREATE TABLE StoppingPattern (
|
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
|
||||||
routeId TEXT NOT NULL REFERENCES Route (id),
|
|
||||||
shapeId TEXT NOT NULL REFERENCES Shape (id),
|
|
||||||
headsign TEXT NOT NULL,
|
|
||||||
wheelchairAccessible INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
insert:
|
|
||||||
INSERT OR REPLACE INTO StoppingPattern VALUES ?;
|
|
||||||
|
|
||||||
get:
|
|
||||||
SELECT * FROM StoppingPattern WHERE id == :id;
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
CREATE TABLE Trip (
|
|
||||||
gtfsId TEXT PRIMARY KEY NOT NULL,
|
|
||||||
patternId INTEGER NOT NULL REFERENCES StoppingPattern (id),
|
|
||||||
serviceId TEXT NOT NULL REFERENCES Service (id),
|
|
||||||
blockId INTEGER,
|
|
||||||
directionId INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_Trip_patternId ON Trip (patternId);
|
|
||||||
CREATE INDEX idx_Trip_serviceId ON Trip (serviceId);
|
|
||||||
|
|
||||||
insert:
|
|
||||||
INSERT OR REPLACE INTO Trip VALUES ?;
|
|
||||||
Binary file not shown.
|
|
@ -1,11 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld
|
|
||||||
|
|
||||||
import app.cash.sqldelight.driver.native.NativeSqliteDriver
|
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
|
|
||||||
actual class DatabaseManager : KoinComponent {
|
|
||||||
actual val database by lazy {
|
|
||||||
val driver = NativeSqliteDriver(BanksiaDatabase.Schema, "${DBNAME}.db")
|
|
||||||
BanksiaDatabase(driver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
package moe.lava.banksia.core.sqld
|
|
||||||
|
|
||||||
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
|
|
||||||
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 java.util.Properties
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
actual class DatabaseManager : KoinComponent {
|
|
||||||
private var driver = connect()
|
|
||||||
actual val database get() = BanksiaDatabase(driver)
|
|
||||||
|
|
||||||
private fun connect(path: String = "./data/${DBNAME}.db") =
|
|
||||||
JdbcSqliteDriver("jdbc:sqlite:${path}", Properties(), BanksiaDatabase.Schema)
|
|
||||||
.apply { execute(null, "PRAGMA journal_mode = OFF;", 0) }
|
|
||||||
|
|
||||||
fun makeAlt() = run {
|
|
||||||
File("./data/${DBNAME}_alt.db").takeIf { it.exists() }?.delete()
|
|
||||||
val driver = connect("./data/${DBNAME}_alt.db")
|
|
||||||
BanksiaDatabase(driver) to { driver.close() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun swap(scope: CoroutineScope = CoroutineScope(Dispatchers.IO)) {
|
|
||||||
val live = File("./data/${DBNAME}.db")
|
|
||||||
val alt = File("./data/${DBNAME}_alt.db")
|
|
||||||
val old = File("./data/${DBNAME}_old.db")
|
|
||||||
|
|
||||||
if (live.takeIf { it.exists() }?.renameTo(old) == false) {
|
|
||||||
error("DatabaseManager", "Failed to rename database from live to old (${live.absolutePath} -> ${old.absolutePath})")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (alt.takeIf { it.exists() }?.renameTo(live) == false) {
|
|
||||||
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 oldDriver = driver
|
|
||||||
driver = connect()
|
|
||||||
|
|
||||||
scope.launch {
|
|
||||||
delay(5000)
|
|
||||||
if (old.takeIf { it.exists() }?.delete() == false) {
|
|
||||||
error("DatabaseManager", "Failed to unlink old database, stray files! (${old.absolutePath})")
|
|
||||||
}
|
|
||||||
oldDriver.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
package moe.lava.banksia.core.endpoints
|
|
||||||
|
|
||||||
object Endpoint
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package moe.lava.banksia.core.model
|
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ServiceException(
|
|
||||||
val serviceId: String,
|
|
||||||
val date: LocalDate,
|
|
||||||
val type: Int,
|
|
||||||
)
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package moe.lava.banksia.core.model
|
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import kotlinx.datetime.LocalDateTime
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class StopTime<T: TimeType>(
|
|
||||||
val patternId: Long,
|
|
||||||
val stopId: String,
|
|
||||||
val time: T,
|
|
||||||
val pickupType: Int,
|
|
||||||
val dropOffType: Int,
|
|
||||||
) {
|
|
||||||
typealias Dated = StopTime<TimeType.Dated>
|
|
||||||
typealias Undated = StopTime<TimeType.Undated>
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
sealed class TimeType {
|
|
||||||
@Serializable
|
|
||||||
data class Undated(
|
|
||||||
val arrival: FutureTime,
|
|
||||||
val departure: FutureTime,
|
|
||||||
) : TimeType()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Dated(
|
|
||||||
val arrival: LocalDateTime,
|
|
||||||
val departure: LocalDateTime,
|
|
||||||
) : TimeType()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TimeType.Undated.atDate(date: LocalDate) = TimeType.Dated(
|
|
||||||
arrival = arrival.atDate(date),
|
|
||||||
departure = departure.atDate(date),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun StopTime<TimeType.Undated>.atDate(date: LocalDate) = StopTime(
|
|
||||||
patternId = patternId,
|
|
||||||
stopId = stopId,
|
|
||||||
time = time.atDate(date),
|
|
||||||
pickupType = pickupType,
|
|
||||||
dropOffType = dropOffType,
|
|
||||||
)
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package moe.lava.banksia.core.model
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class StoppingPattern<T: TimeType>(
|
|
||||||
val id: Long,
|
|
||||||
val routeId: String,
|
|
||||||
val shapeId: String,
|
|
||||||
val headsign: String,
|
|
||||||
val wheelchairAccessible: Boolean,
|
|
||||||
val stoptimes: List<StopTime<T>>,
|
|
||||||
) {
|
|
||||||
typealias Dated = StoppingPattern<TimeType.Dated>
|
|
||||||
typealias Undated = StoppingPattern<TimeType.Undated>
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
package moe.lava.banksia.core.model
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Trip<T: TimeType>(
|
|
||||||
val id: String,
|
|
||||||
val pattern: StoppingPattern<T>,
|
|
||||||
val service: Service,
|
|
||||||
val directionId: Int,
|
|
||||||
val blockId: String?,
|
|
||||||
) {
|
|
||||||
typealias Dated = Trip<TimeType.Dated>
|
|
||||||
typealias Undated = Trip<TimeType.Undated>
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +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.stoptime"
|
|
||||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
|
||||||
|
|
||||||
compilerOptions {
|
|
||||||
jvmTarget.set(JvmTarget.JVM_11)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compilerOptions {
|
|
||||||
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
|
|
||||||
freeCompilerArgs.add("-Xexpect-actual-classes")
|
|
||||||
}
|
|
||||||
|
|
||||||
iosArm64()
|
|
||||||
iosSimulatorArm64()
|
|
||||||
|
|
||||||
jvm()
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
val clientMain by creating {
|
|
||||||
dependsOn(commonMain.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
androidMain.get().dependsOn(clientMain)
|
|
||||||
iosArm64Main.get().dependsOn(clientMain)
|
|
||||||
iosSimulatorArm64Main.get().dependsOn(clientMain)
|
|
||||||
|
|
||||||
androidMain.dependencies {
|
|
||||||
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.sqld)
|
|
||||||
}
|
|
||||||
iosMain.dependencies {
|
|
||||||
implementation(libs.ktor.client.darwin)
|
|
||||||
}
|
|
||||||
jvmMain.dependencies {
|
|
||||||
implementation(libs.koin.ktor)
|
|
||||||
implementation(libs.ktor.server.core)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package moe.lava.banksia.core.data
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.data.repositories.StopTimeRepository
|
|
||||||
import moe.lava.banksia.core.data.sources.stoptime.StopTimeRemoteDataSource
|
|
||||||
import org.koin.core.module.dsl.singleOf
|
|
||||||
import org.koin.dsl.module
|
|
||||||
|
|
||||||
internal actual val platformModule = module {
|
|
||||||
singleOf(::StopTimeRepository)
|
|
||||||
singleOf(::StopTimeRemoteDataSource)
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.repositories
|
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource
|
|
||||||
import moe.lava.banksia.core.data.sources.stoptime.StopTimeRemoteDataSource
|
|
||||||
|
|
||||||
actual class StopTimeRepository internal constructor(
|
|
||||||
private val local: StopTimeLocalDataSource,
|
|
||||||
private val remote: StopTimeRemoteDataSource,
|
|
||||||
) {
|
|
||||||
actual suspend fun getForStop(id: String, date: LocalDate) = flow {
|
|
||||||
emit(local.getAtStop(id, date))
|
|
||||||
|
|
||||||
remote.getAtStop(id, date)
|
|
||||||
.takeIf { it.isNotEmpty() }
|
|
||||||
?.let { emit(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.sources.stoptime
|
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.request.parameter
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import kotlinx.datetime.TimeZone
|
|
||||||
import kotlinx.datetime.todayIn
|
|
||||||
import moe.lava.banksia.core.data.dto.ExtendedStopTime
|
|
||||||
import moe.lava.banksia.core.endpoints.Endpoint
|
|
||||||
import moe.lava.banksia.core.endpoints.stopTimeByStop
|
|
||||||
import kotlin.time.Clock
|
|
||||||
|
|
||||||
internal class StopTimeRemoteDataSource(
|
|
||||||
private val client: HttpClient,
|
|
||||||
) {
|
|
||||||
suspend fun getAtStop(
|
|
||||||
stopId: String,
|
|
||||||
date: LocalDate? = Clock.System.todayIn(TimeZone.currentSystemDefault()),
|
|
||||||
): List<ExtendedStopTime> {
|
|
||||||
return client.get(Endpoint.stopTimeByStop(stopId)) {
|
|
||||||
parameter("date", date)
|
|
||||||
}.body<List<ExtendedStopTime>>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
package moe.lava.banksia.core.data
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource
|
|
||||||
import org.koin.core.module.Module
|
|
||||||
import org.koin.core.module.dsl.singleOf
|
|
||||||
import org.koin.dsl.module
|
|
||||||
|
|
||||||
internal expect val platformModule: Module;
|
|
||||||
|
|
||||||
val stopTimeDataDiModule = module {
|
|
||||||
includes(platformModule)
|
|
||||||
singleOf(::StopTimeLocalDataSource)
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.dto
|
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import moe.lava.banksia.core.model.FutureTime
|
|
||||||
import moe.lava.banksia.core.model.RouteType
|
|
||||||
import moe.lava.banksia.core.model.TimeType
|
|
||||||
import moe.lava.banksia.core.model.atDate
|
|
||||||
import moe.lava.banksia.core.sqld.GetExtendedForStop
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ExtendedStopTime(
|
|
||||||
val patternId: Long,
|
|
||||||
val stopPlatformCode: String?,
|
|
||||||
val time: TimeType.Dated,
|
|
||||||
val headsign: String?,
|
|
||||||
val routeType: RouteType,
|
|
||||||
val routeNumber: String?,
|
|
||||||
val routeName: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: This probably doesn't belong here
|
|
||||||
fun GetExtendedForStop.asModel(date: LocalDate) = ExtendedStopTime(
|
|
||||||
patternId = patternId,
|
|
||||||
stopPlatformCode = stopPlatformCode,
|
|
||||||
time = TimeType.Undated(
|
|
||||||
arrival = FutureTime.fromInt((departureTime + arrivalDelta).toInt()),
|
|
||||||
departure = FutureTime.fromInt(departureTime.toInt()),
|
|
||||||
).atDate(date),
|
|
||||||
headsign = headsign,
|
|
||||||
routeType = RouteType.from(routeType.toInt()),
|
|
||||||
routeNumber = routeNumber,
|
|
||||||
routeName = routeName,
|
|
||||||
)
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.repositories
|
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import kotlinx.datetime.TimeZone
|
|
||||||
import kotlinx.datetime.todayIn
|
|
||||||
import moe.lava.banksia.core.data.dto.ExtendedStopTime
|
|
||||||
import kotlin.time.Clock
|
|
||||||
|
|
||||||
expect class StopTimeRepository {
|
|
||||||
suspend fun getForStop(
|
|
||||||
id: String,
|
|
||||||
date: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault()),
|
|
||||||
): Flow<List<ExtendedStopTime>>
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.sources.stoptime
|
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.IO
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import moe.lava.banksia.core.data.dto.ExtendedStopTime
|
|
||||||
import moe.lava.banksia.core.data.dto.asModel
|
|
||||||
import moe.lava.banksia.core.sqld.StopTimeQueries
|
|
||||||
import moe.lava.banksia.core.util.serialise
|
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
import org.koin.core.component.get
|
|
||||||
|
|
||||||
internal class StopTimeLocalDataSource : KoinComponent {
|
|
||||||
private val queries get() = get<StopTimeQueries>()
|
|
||||||
|
|
||||||
suspend fun getAtStop(stopId: String, date: LocalDate): List<ExtendedStopTime> {
|
|
||||||
return withContext(context = Dispatchers.IO) {
|
|
||||||
queries
|
|
||||||
.getExtendedForStop(
|
|
||||||
listOf(date.dayOfWeek).serialise().toLong(),
|
|
||||||
date.toEpochDays(),
|
|
||||||
stopId,
|
|
||||||
)
|
|
||||||
.executeAsList()
|
|
||||||
.map { it.asModel(date) }
|
|
||||||
.sortedBy { it.time.departure }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
package moe.lava.banksia.core.endpoints
|
|
||||||
|
|
||||||
fun Endpoint.stopTimeByStop(stopId: String) = "stoptimes/by_stop/${stopId}"
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package moe.lava.banksia.core.data
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.data.repositories.StopTimeRepository
|
|
||||||
import org.koin.core.module.dsl.singleOf
|
|
||||||
import org.koin.dsl.module
|
|
||||||
|
|
||||||
internal actual val platformModule = module {
|
|
||||||
singleOf(::StopTimeRepository)
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
package moe.lava.banksia.core.data.repositories
|
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource
|
|
||||||
|
|
||||||
actual class StopTimeRepository internal constructor(
|
|
||||||
private val local: StopTimeLocalDataSource,
|
|
||||||
) {
|
|
||||||
actual suspend fun getForStop(id: String, date: LocalDate) = flow {
|
|
||||||
emit(local.getAtStop(id, date))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package moe.lava.banksia.server.routes
|
|
||||||
|
|
||||||
import io.ktor.server.response.respond
|
|
||||||
import io.ktor.server.routing.Route
|
|
||||||
import io.ktor.server.routing.get
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import kotlinx.datetime.TimeZone
|
|
||||||
import kotlinx.datetime.todayIn
|
|
||||||
import moe.lava.banksia.core.data.repositories.StopTimeRepository
|
|
||||||
import moe.lava.banksia.core.endpoints.Endpoint
|
|
||||||
import moe.lava.banksia.core.endpoints.stopTimeByStop
|
|
||||||
import org.koin.ktor.ext.inject
|
|
||||||
import kotlin.time.Clock
|
|
||||||
|
|
||||||
fun Route.stopTimeRoutes() {
|
|
||||||
val repo by inject<StopTimeRepository>()
|
|
||||||
|
|
||||||
get(Endpoint.stopTimeByStop("{stop_id}")) {
|
|
||||||
val stopId = call.parameters["stop_id"]!!
|
|
||||||
val date = call.queryParameters["date"]
|
|
||||||
?.let { LocalDate.parse(it, LocalDate.Formats.ISO) }
|
|
||||||
?: Clock.System.todayIn(TimeZone.currentSystemDefault())
|
|
||||||
val data = repo.getForStop(stopId, date).first()
|
|
||||||
call.respond(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +1,40 @@
|
||||||
[versions]
|
[versions]
|
||||||
agp = "9.1.0"
|
agp = "9.1.0"
|
||||||
android-compileSdk = "37"
|
android-compileSdk = "36"
|
||||||
android-minSdk = "24"
|
android-minSdk = "24"
|
||||||
android-targetSdk = "37"
|
android-targetSdk = "36"
|
||||||
androidx-activity= "1.13.0"
|
androidx-activityCompose = "1.12.4"
|
||||||
androidx-lifecycle = "2.10.0"
|
androidx-appcompat = "1.7.0"
|
||||||
compose-multiplatform = "1.12.0-alpha02"
|
androidx-constraintlayout = "2.2.1"
|
||||||
|
androidx-core-ktx = "1.15.0"
|
||||||
|
androidx-espresso-core = "3.6.1"
|
||||||
|
androidx-lifecycle = "2.9.6"
|
||||||
|
androidx-material = "1.12.0"
|
||||||
|
androidx-test-junit = "1.2.1"
|
||||||
|
compose-multiplatform = "1.11.0-alpha02"
|
||||||
composeunstyled = "1.49.6"
|
composeunstyled = "1.49.6"
|
||||||
coroutines = "1.10.2"
|
coroutines = "1.10.2"
|
||||||
geo = "0.8.0"
|
geo = "0.8.0"
|
||||||
koin = "4.2.0"
|
junit = "4.13.2"
|
||||||
kotlin = "2.3.20"
|
koin = "4.1.1"
|
||||||
|
kotlin = "2.3.10"
|
||||||
kotlinxDatetime = "0.7.1"
|
kotlinxDatetime = "0.7.1"
|
||||||
kotlinxSerializationCsv = "0.2.18"
|
kotlinxSerializationCsv = "0.2.18"
|
||||||
kotlinxSerialization = "1.10.0"
|
kotlinxSerialization = "1.10.0"
|
||||||
ksp = "2.3.4"
|
ksp = "2.3.4"
|
||||||
ktor = "3.4.1"
|
ktor = "3.4.0"
|
||||||
logback = "1.5.32"
|
logback = "1.5.32"
|
||||||
maplibre = "0.12.1"
|
maplibre = "0.12.1"
|
||||||
material = "1.7.3"
|
material = "1.7.3"
|
||||||
material3 = "1.11.0-alpha07"
|
material3 = "1.11.0-alpha02"
|
||||||
okio = "3.17.0"
|
okio = "3.16.4"
|
||||||
playServicesLocation = "21.3.0"
|
playServicesLocation = "21.3.0"
|
||||||
|
sqlite = "2.6.2"
|
||||||
|
room = "2.8.4"
|
||||||
secretsGradlePlugin = "2.0.1"
|
secretsGradlePlugin = "2.0.1"
|
||||||
sqldelight = "2.3.2"
|
wire = "5.5.0"
|
||||||
wire = "6.1.0"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
|
|
||||||
androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
|
|
||||||
androidx-lifecycle-viewmodel-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
|
|
||||||
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
|
|
||||||
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "compose-multiplatform" }
|
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "compose-multiplatform" }
|
||||||
compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "compose-multiplatform" }
|
compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "compose-multiplatform" }
|
||||||
compose-material-icons-core = { module = "org.jetbrains.compose.material:material-icons-core", version.ref = "material" }
|
compose-material-icons-core = { module = "org.jetbrains.compose.material:material-icons-core", version.ref = "material" }
|
||||||
|
|
@ -40,11 +44,18 @@ compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "compose-mu
|
||||||
compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "compose-multiplatform" }
|
compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "compose-multiplatform" }
|
||||||
compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "compose-multiplatform" }
|
compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "compose-multiplatform" }
|
||||||
composeunstyled = { module = "com.composables:composeunstyled", version.ref = "composeunstyled" }
|
composeunstyled = { module = "com.composables:composeunstyled", version.ref = "composeunstyled" }
|
||||||
|
moko-geo = { module = "dev.icerock.moko:geo", version.ref = "geo" }
|
||||||
|
moko-geo-compose = { module = "dev.icerock.moko:geo-compose", version.ref = "geo" }
|
||||||
|
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
||||||
|
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
|
||||||
|
androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
|
||||||
|
androidx-lifecycle-viewmodel-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
|
||||||
|
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
|
||||||
|
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
|
||||||
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
|
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
|
||||||
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" }
|
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" }
|
||||||
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||||
koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
|
koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
|
||||||
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
|
||||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
|
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
|
||||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
|
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
|
||||||
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
|
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
|
||||||
|
|
@ -62,14 +73,14 @@ ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "k
|
||||||
ktor-server-tests = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" }
|
ktor-server-tests = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" }
|
||||||
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
||||||
maplibre-compose = { module = "org.maplibre.compose:maplibre-compose", version.ref = "maplibre" }
|
maplibre-compose = { module = "org.maplibre.compose:maplibre-compose", version.ref = "maplibre" }
|
||||||
moko-geo = { module = "dev.icerock.moko:geo", version.ref = "geo" }
|
|
||||||
moko-geo-compose = { module = "dev.icerock.moko:geo-compose", version.ref = "geo" }
|
|
||||||
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
|
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
|
||||||
play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "playServicesLocation" }
|
play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "playServicesLocation" }
|
||||||
|
room-common = { group = "androidx.room", name = "room-common", version.ref = "room" }
|
||||||
|
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||||
|
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||||
|
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||||
|
sqlite-bundled = { group = "androidx.sqlite", name = "sqlite-bundled", version.ref = "sqlite" }
|
||||||
secrets-gradle-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "secretsGradlePlugin" }
|
secrets-gradle-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "secretsGradlePlugin" }
|
||||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
|
||||||
sqldelight-driver-jvm = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" }
|
|
||||||
sqldelight-driver-native = { module = "app.cash.sqldelight:native-driver", version.ref = "sqldelight" }
|
|
||||||
ui-backhandler = { module = "org.jetbrains.compose.ui:ui-backhandler", version.ref = "compose-multiplatform" }
|
ui-backhandler = { module = "org.jetbrains.compose.ui:ui-backhandler", version.ref = "compose-multiplatform" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
|
@ -82,6 +93,6 @@ kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref =
|
||||||
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||||
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
|
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
|
||||||
|
room = { id = "androidx.room", version.ref = "room" }
|
||||||
secretsGradle = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin" }
|
secretsGradle = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin" }
|
||||||
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
|
|
||||||
wire = { id = "com.squareup.wire", version.ref = "wire" }
|
wire = { id = "com.squareup.wire", version.ref = "wire" }
|
||||||
|
|
|
||||||
|
|
@ -5,27 +5,15 @@ plugins {
|
||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "moe.lava.banksia.server"
|
group = "moe.lava.banksia"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
application {
|
application {
|
||||||
mainClass.set("moe.lava.banksia.server.ApplicationKt")
|
mainClass.set("moe.lava.banksia.server.ApplicationKt")
|
||||||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}")
|
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
|
||||||
compilerOptions {
|
|
||||||
freeCompilerArgs.add("-Xexplicit-backing-fields")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.core)
|
implementation(projects.shared)
|
||||||
implementation(projects.core.data)
|
|
||||||
implementation(projects.core.sqld)
|
|
||||||
implementation(projects.core.stoptime)
|
|
||||||
implementation(projects.server.gtfs)
|
|
||||||
implementation(projects.server.gtfsRt)
|
|
||||||
|
|
||||||
implementation(libs.logback)
|
implementation(libs.logback)
|
||||||
implementation(libs.koin.core)
|
implementation(libs.koin.core)
|
||||||
implementation(libs.koin.ktor)
|
implementation(libs.koin.ktor)
|
||||||
|
|
@ -38,6 +26,8 @@ dependencies {
|
||||||
implementation(libs.ktor.server.contentnegotiation)
|
implementation(libs.ktor.server.contentnegotiation)
|
||||||
implementation(libs.ktor.server.core)
|
implementation(libs.ktor.server.core)
|
||||||
implementation(libs.ktor.server.netty)
|
implementation(libs.ktor.server.netty)
|
||||||
|
implementation(libs.room.runtime)
|
||||||
|
implementation(libs.sqlite.bundled)
|
||||||
testImplementation(libs.ktor.server.tests)
|
testImplementation(libs.ktor.server.tests)
|
||||||
testImplementation(libs.kotlin.test.junit)
|
testImplementation(libs.kotlin.test.junit)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
plugins {
|
|
||||||
alias(libs.plugins.kotlinJvm)
|
|
||||||
alias(libs.plugins.kotlinSerialization)
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
compilerOptions {
|
|
||||||
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
|
|
||||||
freeCompilerArgs.add("-Xexplicit-backing-fields")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(projects.core)
|
|
||||||
implementation(libs.kotlinx.serialization.csv)
|
|
||||||
implementation(libs.kotlinx.datetime)
|
|
||||||
implementation(libs.ktor.client.contentnegotiation)
|
|
||||||
implementation(libs.ktor.client.core)
|
|
||||||
implementation(libs.ktor.client.okhttp)
|
|
||||||
}
|
|
||||||
|
|
@ -1,388 +0,0 @@
|
||||||
package moe.lava.banksia.server.gtfs
|
|
||||||
|
|
||||||
import com.lightningkite.kotlinx.serialization.csv.CsvFormat
|
|
||||||
import com.lightningkite.kotlinx.serialization.csv.StringDeferringConfig
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.request.prepareRequest
|
|
||||||
import io.ktor.client.request.url
|
|
||||||
import io.ktor.client.statement.bodyAsChannel
|
|
||||||
import io.ktor.util.cio.writeChannel
|
|
||||||
import io.ktor.util.logging.Logger
|
|
||||||
import io.ktor.utils.io.copyAndClose
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.onCompletion
|
|
||||||
import kotlinx.datetime.DayOfWeek
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.modules.EmptySerializersModule
|
|
||||||
import kotlinx.serialization.serializer
|
|
||||||
import moe.lava.banksia.core.Constants
|
|
||||||
import moe.lava.banksia.core.model.FutureTime.Companion.asInt
|
|
||||||
import moe.lava.banksia.core.model.Route
|
|
||||||
import moe.lava.banksia.core.model.RouteType
|
|
||||||
import moe.lava.banksia.core.model.Service
|
|
||||||
import moe.lava.banksia.core.model.ServiceException
|
|
||||||
import moe.lava.banksia.core.model.Shape
|
|
||||||
import moe.lava.banksia.core.model.Stop
|
|
||||||
import moe.lava.banksia.core.model.StopTime
|
|
||||||
import moe.lava.banksia.core.model.StoppingPattern
|
|
||||||
import moe.lava.banksia.core.model.TimeType
|
|
||||||
import moe.lava.banksia.core.model.Trip
|
|
||||||
import moe.lava.banksia.core.util.Point
|
|
||||||
import moe.lava.banksia.server.gtfs.structures.GtfsRoute
|
|
||||||
import moe.lava.banksia.server.gtfs.structures.GtfsService
|
|
||||||
import moe.lava.banksia.server.gtfs.structures.GtfsServiceException
|
|
||||||
import moe.lava.banksia.server.gtfs.structures.GtfsShape
|
|
||||||
import moe.lava.banksia.server.gtfs.structures.GtfsStop
|
|
||||||
import moe.lava.banksia.server.gtfs.structures.GtfsStopTime
|
|
||||||
import moe.lava.banksia.server.gtfs.structures.GtfsTrip
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.security.MessageDigest
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
import kotlin.time.ExperimentalTime
|
|
||||||
|
|
||||||
private typealias StopWithSource = Pair<String, Stop>
|
|
||||||
|
|
||||||
sealed class GtfsData {
|
|
||||||
data class RouteChunk(val routes: List<Route>) : GtfsData()
|
|
||||||
data class ServiceChunk(val services: List<Service>) : GtfsData()
|
|
||||||
data class ServiceExceptionChunk(val exceptions: List<ServiceException>) : GtfsData()
|
|
||||||
data class ShapeChunk(val shapes: List<Shape>) : GtfsData()
|
|
||||||
data class StopChunk(val stops: List<Stop>) : GtfsData()
|
|
||||||
data class TripChunk(val trips: List<Trip.Undated>) : GtfsData()
|
|
||||||
}
|
|
||||||
|
|
||||||
class GtfsParser(
|
|
||||||
private val log: Logger,
|
|
||||||
private val client: HttpClient,
|
|
||||||
) {
|
|
||||||
private val csv = CsvFormat(StringDeferringConfig(EmptySerializersModule()))
|
|
||||||
private val datasetPath = File("/tmp/banksia", "dataset.zip")
|
|
||||||
|
|
||||||
@OptIn(ExperimentalTime::class)
|
|
||||||
suspend fun update(datasetUrl: String): Flow<GtfsData> {
|
|
||||||
val parentDir = datasetPath.parentFile
|
|
||||||
@Suppress("SimplifyBooleanWithConstants", "KotlinConstantConditions")
|
|
||||||
if (parentDir.exists() && !Constants.devMode)
|
|
||||||
parentDir.deleteRecursively()
|
|
||||||
|
|
||||||
parentDir.mkdirs()
|
|
||||||
|
|
||||||
log.info("fetching..")
|
|
||||||
client.prepareRequest {
|
|
||||||
url(datasetUrl)
|
|
||||||
}.execute { resp ->
|
|
||||||
if (!datasetPath.exists())
|
|
||||||
resp.bodyAsChannel().copyAndClose(datasetPath.writeChannel())
|
|
||||||
log.info("fetched!")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("extracting...")
|
|
||||||
@Suppress("KotlinConstantConditions")
|
|
||||||
val files = if (Constants.devMode) {
|
|
||||||
datasetPath.parentFile
|
|
||||||
.listFiles { it.isDirectory }
|
|
||||||
.flatMap { d -> d.listFiles { f -> f.extension == "txt" }.toList() }
|
|
||||||
.ifEmpty { extractAll(datasetPath) }
|
|
||||||
// .filter { it.parentFile.name == "2" }
|
|
||||||
} else {
|
|
||||||
extractAll(datasetPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("parsing...")
|
|
||||||
return parse(files)
|
|
||||||
.onCompletion {
|
|
||||||
@Suppress("KotlinConstantConditions")
|
|
||||||
if (!Constants.devMode) {
|
|
||||||
parentDir.deleteRecursively()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("done!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parse(files: List<File>) = flow {
|
|
||||||
files
|
|
||||||
.filter { it.name == "routes.txt" }
|
|
||||||
.forEach { emit(GtfsData.RouteChunk(parseRoutes(it))) }
|
|
||||||
|
|
||||||
files
|
|
||||||
.filter { it.name == "stops.txt" }
|
|
||||||
.flatMap { parseStops(it) }
|
|
||||||
.let { emit(GtfsData.StopChunk(fixupDuplicateStops(it))) }
|
|
||||||
|
|
||||||
files
|
|
||||||
.filter { it.name == "shapes.txt" }
|
|
||||||
.forEach { emit(GtfsData.ShapeChunk(parseShapes(it))) }
|
|
||||||
|
|
||||||
val services = files
|
|
||||||
.filter { it.name == "calendar.txt" }
|
|
||||||
.flatMap { fd ->
|
|
||||||
parseServices(fd)
|
|
||||||
.also { emit(GtfsData.ServiceChunk(it)) }
|
|
||||||
}
|
|
||||||
.associateBy { it.id }
|
|
||||||
|
|
||||||
files
|
|
||||||
.filter { it.name == "calendar_dates.txt" }
|
|
||||||
.forEach { emit(GtfsData.ServiceExceptionChunk(parseServiceExceptions(it))) }
|
|
||||||
|
|
||||||
val trips = files
|
|
||||||
.filter { it.name == "trips.txt" }
|
|
||||||
.flatMap { fd ->
|
|
||||||
parseTrips(fd, services)
|
|
||||||
}
|
|
||||||
.associateBy { it.id }
|
|
||||||
|
|
||||||
files
|
|
||||||
.filter { it.name == "stop_times.txt" }
|
|
||||||
.forEach { fd ->
|
|
||||||
log.info("parsing stop times for ${fd.parent}...")
|
|
||||||
parseStopTimes(fd) { seq ->
|
|
||||||
val times = ArrayList<Pair<String, StopTime.Undated>>(1000100)
|
|
||||||
seq.forEach { pair ->
|
|
||||||
val (_, stoptime) = pair
|
|
||||||
if (times.size > 1000000 && stoptime.patternId == 1L) {
|
|
||||||
emit(GtfsData.TripChunk(processStoptimes(trips, times)))
|
|
||||||
times.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
times.add(pair)
|
|
||||||
}
|
|
||||||
emit(GtfsData.TripChunk(processStoptimes(trips, times)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hashCalc(headsign: String, stops: List<StopTime.Undated>): Long {
|
|
||||||
val inst = MessageDigest.getInstance("SHA-256")
|
|
||||||
inst.update(headsign.toByteArray())
|
|
||||||
stops.forEach {
|
|
||||||
inst.update(it.stopId.toByteArray())
|
|
||||||
val dint = it.time.departure.asInt()
|
|
||||||
inst.update((dint).toByte())
|
|
||||||
inst.update((dint shr 8).toByte())
|
|
||||||
val aint = it.time.arrival.asInt()
|
|
||||||
inst.update((aint).toByte())
|
|
||||||
inst.update((aint shr 8).toByte())
|
|
||||||
}
|
|
||||||
|
|
||||||
val buf = inst.digest().slice(0..7).toByteArray()
|
|
||||||
buf[0] = 0
|
|
||||||
buf[1] = 0
|
|
||||||
return ByteBuffer.wrap(buf).long
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processStoptimes(trips: Map<String, Trip.Undated>, times: ArrayList<Pair<String, StopTime.Undated>>) =
|
|
||||||
times.groupBy { it.first }
|
|
||||||
.map { (tripId, pairs) ->
|
|
||||||
val trip = trips[tripId]!!
|
|
||||||
val stoptimes = pairs.map { it.second }
|
|
||||||
val hash = hashCalc(trip.pattern.headsign, stoptimes)
|
|
||||||
trip.copy(pattern = trip.pattern.copy(
|
|
||||||
id = hash,
|
|
||||||
stoptimes = stoptimes.map { it.copy(patternId = hash) }
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseRoutes(fd: File) =
|
|
||||||
fd.parseCsv<GtfsRoute>()
|
|
||||||
.map { with(it) {
|
|
||||||
Route(
|
|
||||||
id = route_id,
|
|
||||||
type = RouteType.from(fd.parentFile.name.toInt()),
|
|
||||||
number = route_short_name,
|
|
||||||
name = route_long_name,
|
|
||||||
)
|
|
||||||
} }
|
|
||||||
|
|
||||||
private fun parseShapes(fd: File) =
|
|
||||||
fd.parseCsv<GtfsShape>()
|
|
||||||
.groupBy { it.shape_id }
|
|
||||||
.map { (id, group) ->
|
|
||||||
val points = group
|
|
||||||
.sortedBy { it.shape_pt_sequence }
|
|
||||||
.map { Point(it.shape_pt_lat, it.shape_pt_lon) }
|
|
||||||
|
|
||||||
Shape(id, points)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseStops(fd: File): List<StopWithSource> =
|
|
||||||
fd.parseCsv<GtfsStop>()
|
|
||||||
.map { with(it) {
|
|
||||||
fd.parentFile.name to Stop(
|
|
||||||
id = stop_id,
|
|
||||||
name = stop_name,
|
|
||||||
pos = Point(stop_lat, stop_lon),
|
|
||||||
parent = parent_station.ifEmpty { null },
|
|
||||||
hasWheelChairBoarding = wheelchair_boarding == "1",
|
|
||||||
level = level_id.ifEmpty { null },
|
|
||||||
platformCode = platform_code.ifEmpty { null },
|
|
||||||
)
|
|
||||||
} }
|
|
||||||
|
|
||||||
private inline fun parseStopTimes(fd: File, block: (Sequence<Pair<String, StopTime.Undated>>) -> Unit) =
|
|
||||||
fd.parseCsvSequence<GtfsStopTime> { seq ->
|
|
||||||
seq
|
|
||||||
.map { with(it) {
|
|
||||||
it.trip_id to StopTime(
|
|
||||||
patternId = stop_sequence,
|
|
||||||
stopId = stop_id,
|
|
||||||
time = TimeType.Undated(
|
|
||||||
arrival = GtfsStopTime.parseGtfsTime(arrival_time),
|
|
||||||
departure = GtfsStopTime.parseGtfsTime(departure_time),
|
|
||||||
),
|
|
||||||
pickupType = pickup_type,
|
|
||||||
dropOffType = drop_off_type,
|
|
||||||
)
|
|
||||||
} }
|
|
||||||
.let { block(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseServices(fd: File) =
|
|
||||||
fd.parseCsv<GtfsService>()
|
|
||||||
.map { with(it) {
|
|
||||||
val days = buildList {
|
|
||||||
if (monday == 1) add(DayOfWeek.MONDAY)
|
|
||||||
if (tuesday == 1) add(DayOfWeek.TUESDAY)
|
|
||||||
if (wednesday == 1) add(DayOfWeek.WEDNESDAY)
|
|
||||||
if (thursday == 1) add(DayOfWeek.THURSDAY)
|
|
||||||
if (friday == 1) add(DayOfWeek.FRIDAY)
|
|
||||||
if (saturday == 1) add(DayOfWeek.SATURDAY)
|
|
||||||
if (sunday == 1) add(DayOfWeek.SUNDAY)
|
|
||||||
}
|
|
||||||
Service(
|
|
||||||
id = "${fd.parentFile.name}_${service_id}",
|
|
||||||
days = days,
|
|
||||||
start = LocalDate.parse(start_date, LocalDate.Formats.ISO_BASIC),
|
|
||||||
end = LocalDate.parse(end_date, LocalDate.Formats.ISO_BASIC),
|
|
||||||
)
|
|
||||||
} }
|
|
||||||
|
|
||||||
private fun parseServiceExceptions(fd: File) =
|
|
||||||
fd.parseCsv<GtfsServiceException>()
|
|
||||||
.map { with(it) {
|
|
||||||
ServiceException(
|
|
||||||
serviceId = "${fd.parentFile.name}_${service_id}",
|
|
||||||
date = LocalDate.parse(date, LocalDate.Formats.ISO_BASIC),
|
|
||||||
type = exception_type,
|
|
||||||
)
|
|
||||||
} }
|
|
||||||
|
|
||||||
private fun parseTrips(fd: File, services: Map<String, Service>) =
|
|
||||||
fd.parseCsv<GtfsTrip>()
|
|
||||||
.map { with(it) {
|
|
||||||
Trip.Undated(
|
|
||||||
id = trip_id,
|
|
||||||
pattern = StoppingPattern(
|
|
||||||
id = 0,
|
|
||||||
routeId = route_id,
|
|
||||||
shapeId = shape_id,
|
|
||||||
headsign = trip_headsign,
|
|
||||||
wheelchairAccessible = wheelchair_accessible == "1",
|
|
||||||
stoptimes = listOf()
|
|
||||||
),
|
|
||||||
service = services["${fd.parentFile.name}_${service_id}"]!!,
|
|
||||||
directionId = direction_id.toInt(),
|
|
||||||
blockId = block_id.ifEmpty { null },
|
|
||||||
)
|
|
||||||
} }
|
|
||||||
|
|
||||||
private fun extract(fd: File): List<File> {
|
|
||||||
val outputs = mutableListOf<File>()
|
|
||||||
ZipFile(fd).use { zip ->
|
|
||||||
for (entry in zip.entries()) {
|
|
||||||
zip.getInputStream(entry).use { input ->
|
|
||||||
val out = File(fd.parentFile, entry.name)
|
|
||||||
out.parentFile.mkdirs()
|
|
||||||
out.outputStream().use { output ->
|
|
||||||
input.copyTo(output)
|
|
||||||
}
|
|
||||||
outputs.add(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outputs
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun extractAll(fd: File) = extract(fd).flatMap(::extract)
|
|
||||||
|
|
||||||
private inline fun <reified T> File.parseCsv(): List<T> = this
|
|
||||||
.readText()
|
|
||||||
.replace("\uFEFF", "") // remove bom
|
|
||||||
.replace("\r\n", "\n") // crlf -> lf
|
|
||||||
.let { csv.decodeFromString(it) }
|
|
||||||
|
|
||||||
private inline fun <reified T> File.parseCsvSequence(block: (Sequence<T>) -> Unit) = this
|
|
||||||
.bufferedReader()
|
|
||||||
.use { reader ->
|
|
||||||
val iter = object : CharIterator() {
|
|
||||||
var next: Char? = null
|
|
||||||
override fun nextChar(): Char {
|
|
||||||
if (!hasNext()) {
|
|
||||||
throw NoSuchElementException()
|
|
||||||
}
|
|
||||||
val ret = next!!
|
|
||||||
next = null
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
override fun hasNext(): Boolean {
|
|
||||||
if (next == null) {
|
|
||||||
do {
|
|
||||||
next = null
|
|
||||||
val new = reader.read()
|
|
||||||
if (new != -1) {
|
|
||||||
next = new.toChar()
|
|
||||||
}
|
|
||||||
} while (next == '\uFEFF' || next == '\r')
|
|
||||||
}
|
|
||||||
return next != null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
block(csv.decodeToSequence(iter, csv.serializersModule.serializer()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type priority used to resolve duplicates, preferring the first one in the chain
|
|
||||||
private val typePriorityRanking = listOf(
|
|
||||||
RouteType.MetroTrain,
|
|
||||||
RouteType.RegionalTrain,
|
|
||||||
RouteType.MetroTram,
|
|
||||||
RouteType.MetroBus,
|
|
||||||
RouteType.RegionalBus,
|
|
||||||
RouteType.SkyBus,
|
|
||||||
).map { it.value.toString() }
|
|
||||||
|
|
||||||
@Suppress("LoggingStringTemplateAsArgument") // ?
|
|
||||||
private fun fixupDuplicateStops(stops: List<StopWithSource>): List<Stop> {
|
|
||||||
return stops
|
|
||||||
.groupBy { (_, stops) -> stops.id }
|
|
||||||
.map { (id, stops) ->
|
|
||||||
// Just return it if no duplicate
|
|
||||||
if (stops.size == 1) return@map stops[0].second
|
|
||||||
|
|
||||||
// Just return the first one if all the stops' data match
|
|
||||||
if (stops.withIndex().all { (idx, stop) -> idx == 0 || stop.second == stops[idx - 1].second })
|
|
||||||
return@map stops[0].second
|
|
||||||
|
|
||||||
// Find first stop ordered by the types
|
|
||||||
val res = typePriorityRanking
|
|
||||||
.firstNotNullOfOrNull { type ->
|
|
||||||
stops.find { it.first == type }
|
|
||||||
}
|
|
||||||
|
|
||||||
val (_, stop) = if (res == null) {
|
|
||||||
log.warn("Cannot resolve duplicate stop ${id}, using first one")
|
|
||||||
stops.forEach { (type, stop) -> log.warn(" - ($type): $stop") }
|
|
||||||
stops[0]
|
|
||||||
} else {
|
|
||||||
log.debug("Resolving $id for type ${res.first}")
|
|
||||||
stops.forEach { (type, stop) -> log.debug("${if (res.first == type) "*" else " "} - ($type): $stop") }
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
stop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package moe.lava.banksia.server.gtfs.structures
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
|
||||||
@Serializable
|
|
||||||
internal data class GtfsServiceException(
|
|
||||||
val service_id: String,
|
|
||||||
val date: String,
|
|
||||||
val exception_type: Int,
|
|
||||||
)
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
plugins {
|
|
||||||
alias(libs.plugins.kotlinJvm)
|
|
||||||
alias(libs.plugins.kotlinSerialization)
|
|
||||||
alias(libs.plugins.wire)
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
compilerOptions {
|
|
||||||
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
|
|
||||||
freeCompilerArgs.add("-Xexplicit-backing-fields")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(projects.core)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
wire {
|
|
||||||
sourcePath {
|
|
||||||
srcDir("src/main/proto")
|
|
||||||
}
|
|
||||||
kotlin {}
|
|
||||||
}
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
package moe.lava.banksia.server.gtfsrt
|
|
||||||
|
|
||||||
import com.google.transit.realtime.FeedMessage
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.datetime.TimeZone
|
|
||||||
import kotlinx.datetime.toLocalDateTime
|
|
||||||
import moe.lava.banksia.core.util.log
|
|
||||||
import java.io.File
|
|
||||||
import kotlin.time.Instant
|
|
||||||
|
|
||||||
private const val BASE_DIR = "./data/gtfsr-archive/"
|
|
||||||
|
|
||||||
internal class GtfsrtArchiver {
|
|
||||||
private var started = false
|
|
||||||
|
|
||||||
suspend fun start(flow: SharedFlow<Pair<String, ByteArray>>) {
|
|
||||||
if (started) {
|
|
||||||
log("GtfsrtArchiver", "Tried to start when already started")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
started = true
|
|
||||||
coroutineScope {
|
|
||||||
launch { compressJob() }
|
|
||||||
|
|
||||||
flow.collect { (type, rawData) ->
|
|
||||||
val data = try {
|
|
||||||
FeedMessage.ADAPTER.decode(rawData)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
log("gtfsr $type", "Failed to parse proto: $e")
|
|
||||||
return@collect
|
|
||||||
}
|
|
||||||
val timestamp = data.header_.timestamp
|
|
||||||
?: return@collect log("gtfsr $type", "Failed to read proto timestamp")
|
|
||||||
|
|
||||||
val time = Instant.fromEpochSeconds(timestamp).toLocalDateTime(TimeZone.currentSystemDefault())
|
|
||||||
|
|
||||||
val (prevWeek, prevDay) = (time.dayOfYear - 1) / 7 to (time.dayOfYear - 1) % 7
|
|
||||||
val (nextWeek, nextDay) = time.dayOfYear / 7 to time.dayOfYear % 7
|
|
||||||
|
|
||||||
val base = File(BASE_DIR, type)
|
|
||||||
val previousParent = File(base, "${time.year}-${prevWeek.toString().padStart(2, '0')}/${prevDay}")
|
|
||||||
val currentParent = File(base, "${time.year}-${nextWeek.toString().padStart(2, '0')}/${nextDay}")
|
|
||||||
val target = File(currentParent, "${timestamp}.proto")
|
|
||||||
|
|
||||||
if (previousParent.isDirectory) {
|
|
||||||
enqueueCompression(previousParent)
|
|
||||||
if (prevWeek != nextWeek) {
|
|
||||||
enqueueCompression(previousParent.parentFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!target.exists()) {
|
|
||||||
try {
|
|
||||||
if (!target.parentFile.isDirectory) {
|
|
||||||
target.parentFile.mkdirs()
|
|
||||||
}
|
|
||||||
target.writeBytes(rawData)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
log("gtfsr $type", "Failed to write ${target}: $e")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val cqueue = mutableSetOf<File>()
|
|
||||||
private val ignore = mutableSetOf<File>()
|
|
||||||
private val cmut = Mutex()
|
|
||||||
private suspend fun enqueueCompression(fd: File) {
|
|
||||||
cmut.withLock { cqueue.add(fd) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun compressJob() {
|
|
||||||
while(true) {
|
|
||||||
while(true) {
|
|
||||||
val next = cmut.withLock { cqueue.firstOrNull() }
|
|
||||||
?: break
|
|
||||||
if (!next.isDirectory) {
|
|
||||||
cmut.withLock { cqueue.remove(next) }
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (next in ignore) continue
|
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val proc = ProcessBuilder(
|
|
||||||
"tar", "-acf",
|
|
||||||
"${next.absolutePath}.tar.zst",
|
|
||||||
next.absolutePath
|
|
||||||
).start()
|
|
||||||
val exitCode = proc.waitFor()
|
|
||||||
if (exitCode == 0) {
|
|
||||||
log("CompressJob", "Compressed ${next.absolutePath} to ${next.absolutePath}.tar.zst")
|
|
||||||
if (next.deleteRecursively()) {
|
|
||||||
cmut.withLock { cqueue.remove(next) }
|
|
||||||
} else {
|
|
||||||
log("CompressJob", "Failed to delete $next")
|
|
||||||
ignore.add(next)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val msg = proc.errorStream.readAllBytes().decodeToString()
|
|
||||||
log("CompressJob", "Failed to delete $next (exit code $exitCode")
|
|
||||||
log("CompressJob", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delay(30000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
package moe.lava.banksia.server.gtfsrt
|
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.request.header
|
|
||||||
import io.ktor.client.request.url
|
|
||||||
import io.ktor.client.statement.bodyAsText
|
|
||||||
import io.ktor.client.statement.readRawBytes
|
|
||||||
import io.ktor.http.isSuccess
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
|
||||||
import kotlinx.coroutines.joinAll
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import moe.lava.banksia.core.Constants
|
|
||||||
import moe.lava.banksia.core.util.LogScope
|
|
||||||
import moe.lava.banksia.core.util.log
|
|
||||||
|
|
||||||
private val types = arrayOf(
|
|
||||||
"metro/trip-updates",
|
|
||||||
"metro/vehicle-positions",
|
|
||||||
"metro/service-alerts",
|
|
||||||
"tram/trip-updates",
|
|
||||||
"tram/vehicle-positions",
|
|
||||||
"tram/service-alerts",
|
|
||||||
"bus/trip-updates",
|
|
||||||
"bus/vehicle-positions",
|
|
||||||
"vline/trip-updates",
|
|
||||||
"vline/vehicle-positions",
|
|
||||||
)
|
|
||||||
|
|
||||||
class GtfsrtService(
|
|
||||||
private val client: HttpClient,
|
|
||||||
) {
|
|
||||||
private val archiver = GtfsrtArchiver()
|
|
||||||
private var started = false
|
|
||||||
|
|
||||||
internal val rawMessages: SharedFlow<Pair<String, ByteArray>>
|
|
||||||
field = MutableSharedFlow<Pair<String, ByteArray>>()
|
|
||||||
|
|
||||||
fun start(
|
|
||||||
scope: CoroutineScope,
|
|
||||||
enableArchiving: Boolean = false,
|
|
||||||
) {
|
|
||||||
if (started) {
|
|
||||||
log("GtfsrtService", "Tried to start when already started")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enableArchiving) {
|
|
||||||
scope.launch { archiver.start(rawMessages) }
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.launch { fetch() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun fetch() {
|
|
||||||
coroutineScope {
|
|
||||||
types.map { type ->
|
|
||||||
launch(context = Dispatchers.IO) {
|
|
||||||
val logger = LogScope("gtfsr $type")
|
|
||||||
try {
|
|
||||||
val res = client.get {
|
|
||||||
url("https://api.opendata.transport.vic.gov.au/opendata/public-transport/gtfs/realtime/v1/${type}")
|
|
||||||
header("KeyId", Constants.opendataKey)
|
|
||||||
}
|
|
||||||
if (!res.status.isSuccess()) {
|
|
||||||
logger.log("${res.status} | ${res.bodyAsText()}")
|
|
||||||
} else {
|
|
||||||
val bytes = res.readRawBytes()
|
|
||||||
rawMessages.emit(type to bytes)
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
logger.log("$e")
|
|
||||||
logger.log(e.stackTraceToString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.joinAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(10000)
|
|
||||||
fetch()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -15,59 +15,47 @@ import io.ktor.server.routing.routing
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import moe.lava.banksia.core.Constants
|
import kotlinx.datetime.LocalDate
|
||||||
import moe.lava.banksia.core.sqld.RouteQueries
|
import kotlinx.datetime.TimeZone
|
||||||
import moe.lava.banksia.core.sqld.StopQueries
|
import kotlinx.datetime.todayIn
|
||||||
import moe.lava.banksia.core.sqld.mappers.asModel
|
import moe.lava.banksia.Constants
|
||||||
|
import moe.lava.banksia.di.CommonModules
|
||||||
|
import moe.lava.banksia.model.atDate
|
||||||
|
import moe.lava.banksia.room.dao.RouteDao
|
||||||
|
import moe.lava.banksia.room.dao.StopDao
|
||||||
|
import moe.lava.banksia.room.dao.StopTimeDao
|
||||||
|
import moe.lava.banksia.room.dao.VersionMetadataDao
|
||||||
import moe.lava.banksia.server.di.ServerModules
|
import moe.lava.banksia.server.di.ServerModules
|
||||||
import moe.lava.banksia.server.gtfsrt.GtfsrtService
|
import moe.lava.banksia.server.gtfs.GtfsHandler
|
||||||
import moe.lava.banksia.server.routes.stopTimeRoutes
|
import moe.lava.banksia.server.gtfsr.GtfsrService
|
||||||
|
import moe.lava.banksia.util.serialise
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import org.koin.ktor.ext.get
|
import org.koin.ktor.ext.inject
|
||||||
import org.koin.ktor.plugin.Koin
|
import org.koin.ktor.plugin.Koin
|
||||||
|
import kotlin.time.Clock
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
if (System.getenv("BANKSIA_PRODUCTION") == "1") Constants.devMode = false
|
|
||||||
|
|
||||||
embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module)
|
embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module)
|
||||||
.start(wait = true)
|
.start(wait = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Application.module() {
|
fun Application.module() {
|
||||||
log.info("devMode: ${Constants.devMode}")
|
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
json()
|
json()
|
||||||
}
|
}
|
||||||
install(Koin) {
|
install(Koin) {
|
||||||
modules(module { single { log } })
|
modules(module { single { log } })
|
||||||
modules(ServerModules)
|
modules(CommonModules, ServerModules)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("KotlinConstantConditions")
|
@Suppress("KotlinConstantConditions")
|
||||||
launch { get<GtfsrtService>().start(this, !Constants.devMode) }
|
if (!Constants.devMode) {
|
||||||
|
val gtfsr by inject<GtfsrService>()
|
||||||
|
launch { gtfsr.start() }
|
||||||
|
}
|
||||||
|
|
||||||
routing {
|
routing {
|
||||||
stopTimeRoutes()
|
get("/update") {
|
||||||
|
|
||||||
if (Constants.devMode) {
|
|
||||||
get("/fixup") {
|
|
||||||
call.respondText("received")
|
|
||||||
get<GtfsDataFixer>().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<GtfsDataFixer>().addParentsToStops()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
get("/manage/update") {
|
|
||||||
val key = call.parameters["key"]
|
val key = call.parameters["key"]
|
||||||
if (key != Constants.updateKey) {
|
if (key != Constants.updateKey) {
|
||||||
call.respond(HttpStatusCode.Forbidden)
|
call.respond(HttpStatusCode.Forbidden)
|
||||||
|
|
@ -79,14 +67,30 @@ fun Application.module() {
|
||||||
?: "https://opendata.transport.vic.gov.au/dataset/3f4e292e-7f8a-4ffe-831f-1953be0fe448/resource/${datasetUuid}/download/gtfs.zip"
|
?: "https://opendata.transport.vic.gov.au/dataset/3f4e292e-7f8a-4ffe-831f-1953be0fe448/resource/${datasetUuid}/download/gtfs.zip"
|
||||||
call.respondText("received")
|
call.respondText("received")
|
||||||
launch(context = Dispatchers.IO) {
|
launch(context = Dispatchers.IO) {
|
||||||
get<GtfsImporter>().import(datasetUrl)
|
val handler by inject<GtfsHandler>()
|
||||||
get<GtfsDataFixer>().addParentsToStops()
|
handler.update(datasetUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/metadata/{type?}") {
|
||||||
|
val dao by inject<VersionMetadataDao>()
|
||||||
|
val type = call.parameters["type"]
|
||||||
|
if (type == null) {
|
||||||
|
call.respond(dao.getAll().map { it.asModel() })
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
|
||||||
|
val data = dao.get(type)?.asModel()
|
||||||
|
if (data == null) {
|
||||||
|
call.respond(HttpStatusCode.NotFound)
|
||||||
|
} else {
|
||||||
|
call.respond(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get("/routes") {
|
get("/routes") {
|
||||||
val routes = withContext(context = Dispatchers.IO) {
|
val routes = withContext(context = Dispatchers.IO) {
|
||||||
get<RouteQueries>().getAll().executeAsList()
|
inject<RouteDao>().value.getAll()
|
||||||
}
|
}
|
||||||
val res = routes.map { it.asModel() }
|
val res = routes.map { it.asModel() }
|
||||||
call.respond(res)
|
call.respond(res)
|
||||||
|
|
@ -94,17 +98,16 @@ fun Application.module() {
|
||||||
get("/routes/{route_id}") {
|
get("/routes/{route_id}") {
|
||||||
val routeId = call.parameters["route_id"]!!
|
val routeId = call.parameters["route_id"]!!
|
||||||
val route = withContext(context = Dispatchers.IO) {
|
val route = withContext(context = Dispatchers.IO) {
|
||||||
get<RouteQueries>().get(routeId).executeAsOneOrNull()
|
inject<RouteDao>().value.get(routeId)
|
||||||
}
|
}
|
||||||
if (route != null) {
|
if (route != null)
|
||||||
call.respond(route.asModel())
|
call.respond(route.asModel())
|
||||||
} else {
|
else
|
||||||
call.respond(HttpStatusCode.NotFound)
|
call.respond(HttpStatusCode.NotFound)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
get("/stops") {
|
get("/stops") {
|
||||||
val routes = withContext(context = Dispatchers.IO) {
|
val routes = withContext(context = Dispatchers.IO) {
|
||||||
get<StopQueries>().getAll().executeAsList()
|
inject<StopDao>().value.getAll()
|
||||||
}
|
}
|
||||||
val res = routes.map { it.asModel() }
|
val res = routes.map { it.asModel() }
|
||||||
call.respond(res)
|
call.respond(res)
|
||||||
|
|
@ -112,26 +115,56 @@ fun Application.module() {
|
||||||
get("/stops/{stop_id}") {
|
get("/stops/{stop_id}") {
|
||||||
val stopId = call.parameters["stop_id"]!!
|
val stopId = call.parameters["stop_id"]!!
|
||||||
val stop = withContext(context = Dispatchers.IO) {
|
val stop = withContext(context = Dispatchers.IO) {
|
||||||
get<StopQueries>().get(stopId).executeAsOneOrNull()
|
inject<StopDao>().value.get(stopId)
|
||||||
}
|
}
|
||||||
if (stop != null) {
|
if (stop != null)
|
||||||
call.respond(stop.asModel())
|
call.respond(stop.asModel())
|
||||||
} else {
|
else
|
||||||
call.respond(HttpStatusCode.NotFound)
|
call.respond(HttpStatusCode.NotFound)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
get("/route_stops/{route_id}") {
|
get("/route_stops/{route_id}") {
|
||||||
val routeId = call.parameters["route_id"]!!
|
val routeId = call.parameters["route_id"]!!
|
||||||
val useParent = call.queryParameters["parent"] !in listOf("false", "0")
|
val useParent = call.queryParameters["parent"] in listOf("true", "1")
|
||||||
val stops = withContext(Dispatchers.IO) {
|
val stops = withContext(Dispatchers.IO) {
|
||||||
val queries = get<StopQueries>()
|
val routeDao by inject<RouteDao>()
|
||||||
if (useParent) {
|
if (useParent)
|
||||||
queries.getParentsByRoute(routeId).executeAsList()
|
routeDao.stopsParent(routeId)
|
||||||
} else {
|
else
|
||||||
queries.getByRoute(routeId).executeAsList()
|
routeDao.stops(routeId)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
call.respond(stops.map { it.asModel() })
|
call.respond(stops.map { it.asModel() })
|
||||||
|
// val stops = withContext(Dispatchers.IO) {
|
||||||
|
// val stopDao by inject<StopDao>()
|
||||||
|
// val stopTimeDao by inject<StopTimeDao>()
|
||||||
|
// val tripDao by inject<TripDao>()
|
||||||
|
//
|
||||||
|
// tripDao.getByRoute(routeId)
|
||||||
|
// .map { it.id }
|
||||||
|
// .let { stopTimeDao.get(it) }
|
||||||
|
// .flatMap { it.asModel().stopInfos }
|
||||||
|
// .map { it.stopId }
|
||||||
|
// .let { stopDao.get(it) }
|
||||||
|
// .map { it.asModel() }
|
||||||
|
// }
|
||||||
|
// call.respond(stops)
|
||||||
|
|
||||||
|
}
|
||||||
|
get("/stoptimes/by_stop/{stop_id}") {
|
||||||
|
val stopId = call.parameters["stop_id"]!!
|
||||||
|
val date = call.queryParameters["date"]
|
||||||
|
?.let { LocalDate.parse(it, LocalDate.Formats.ISO) }
|
||||||
|
?: Clock.System.todayIn(TimeZone.currentSystemDefault())
|
||||||
|
val times = withContext(context = Dispatchers.IO) {
|
||||||
|
inject<StopTimeDao>().value
|
||||||
|
.getForStopDated(
|
||||||
|
stopId,
|
||||||
|
listOf(date.dayOfWeek).serialise(),
|
||||||
|
date.toEpochDays().toInt(),
|
||||||
|
)
|
||||||
|
.map { it.asModel().atDate(date) }
|
||||||
|
.sortedBy { it.departureTime }
|
||||||
|
}
|
||||||
|
call.respond(times)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package moe.lava.banksia.server
|
|
||||||
|
|
||||||
import moe.lava.banksia.core.sqld.BanksiaDatabase
|
|
||||||
import moe.lava.banksia.core.util.log
|
|
||||||
import java.security.MessageDigest
|
|
||||||
import moe.lava.banksia.core.sqld.Stop as DbStop
|
|
||||||
|
|
||||||
class GtfsDataFixer(
|
|
||||||
private val database: BanksiaDatabase,
|
|
||||||
) {
|
|
||||||
fun addParentsToStops() {
|
|
||||||
val queries = database.stopQueries
|
|
||||||
val stops = queries.getAllParentless().executeAsList()
|
|
||||||
stops
|
|
||||||
.groupBy { it.name.split("/")[0] }
|
|
||||||
.filter { (_, stops) -> stops.size > 1 }
|
|
||||||
.forEach { (name, stops) ->
|
|
||||||
val avgLat = stops.map { it.lat }.average()
|
|
||||||
val avgLng = stops.map { it.lng }.average()
|
|
||||||
val hash = name.sha256().substring(0, 7)
|
|
||||||
val parentId = "bsia:df1:$hash"
|
|
||||||
val parent = DbStop(
|
|
||||||
id = parentId,
|
|
||||||
name = name,
|
|
||||||
lat = avgLat,
|
|
||||||
lng = avgLng,
|
|
||||||
parent = null,
|
|
||||||
hasWheelChairBoarding = if (stops.all { it.hasWheelChairBoarding == 1L }) 1L else 0L,
|
|
||||||
level = "",
|
|
||||||
platformCode = "",
|
|
||||||
)
|
|
||||||
log("datafixer", "inserting ${parentId} for ${stops.size} children")
|
|
||||||
queries.transaction {
|
|
||||||
queries.insert(parent)
|
|
||||||
queries.updateParents(parentId, stops.map { it.id })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.sha256() =
|
|
||||||
MessageDigest
|
|
||||||
.getInstance("SHA-256")
|
|
||||||
.digest(this.toByteArray())
|
|
||||||
.joinToString("") { "%02x".format(it) }
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
package moe.lava.banksia.server
|
|
||||||
|
|
||||||
import io.ktor.util.logging.Logger
|
|
||||||
import moe.lava.banksia.core.model.Route
|
|
||||||
import moe.lava.banksia.core.model.Service
|
|
||||||
import moe.lava.banksia.core.model.ServiceException
|
|
||||||
import moe.lava.banksia.core.model.Shape
|
|
||||||
import moe.lava.banksia.core.model.Stop
|
|
||||||
import moe.lava.banksia.core.model.Trip
|
|
||||||
import moe.lava.banksia.core.sqld.DatabaseManager
|
|
||||||
import moe.lava.banksia.core.sqld.mappers.asDb
|
|
||||||
import moe.lava.banksia.server.gtfs.GtfsData
|
|
||||||
import moe.lava.banksia.server.gtfs.GtfsParser
|
|
||||||
import kotlin.time.Clock
|
|
||||||
import moe.lava.banksia.core.sqld.BanksiaDatabase as Database
|
|
||||||
|
|
||||||
class GtfsImporter(
|
|
||||||
private val parser: GtfsParser,
|
|
||||||
private val dbm: DatabaseManager,
|
|
||||||
private val log: Logger,
|
|
||||||
) {
|
|
||||||
suspend fun import(url: String, date: Long = Clock.System.now().epochSeconds) {
|
|
||||||
val (database, close) = dbm.makeAlt()
|
|
||||||
|
|
||||||
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.TripChunk -> database.addTrips(chunk.trips)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close()
|
|
||||||
dbm.swap()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Database.addRoutes(routes: List<Route>) {
|
|
||||||
log.info("inserting routes...")
|
|
||||||
routeQueries.transaction {
|
|
||||||
routes.forEach {
|
|
||||||
routeQueries.insert(it.asDb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.info("done")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Database.addServices(services: List<Service>) {
|
|
||||||
log.info("inserting services...")
|
|
||||||
serviceQueries.transaction {
|
|
||||||
services.forEach {
|
|
||||||
serviceQueries.insert(it.asDb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.info("done")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Database.addServiceExceptions(exceptions: List<ServiceException>) {
|
|
||||||
log.info("inserting exceptions...")
|
|
||||||
serviceExceptionQueries.transaction {
|
|
||||||
exceptions.forEach {
|
|
||||||
serviceExceptionQueries.insert(it.asDb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.info("done")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Database.addShapes(shapes: List<Shape>) {
|
|
||||||
log.info("inserting shapes...")
|
|
||||||
shapeQueries.transaction {
|
|
||||||
shapes.forEach {
|
|
||||||
shapeQueries.insert(it.asDb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.info("done")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Database.addStops(stops: List<Stop>) {
|
|
||||||
log.info("inserting stops...")
|
|
||||||
stops
|
|
||||||
.groupBy { it.id }
|
|
||||||
.forEach { (id, gstops) ->
|
|
||||||
if (gstops.size > 1) {
|
|
||||||
if (gstops.withIndex().any { (i, stop) -> i != 0 && stop != gstops[i - 1] }) {
|
|
||||||
gstops.forEach {
|
|
||||||
log.warn("duplicate $id: $it")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stopQueries.transaction {
|
|
||||||
stops.forEach {
|
|
||||||
stopQueries.insert(it.asDb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.info("done")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Database.addTrips(trips: List<Trip.Undated>) {
|
|
||||||
log.info("inserting ${trips.size} trips...")
|
|
||||||
transaction {
|
|
||||||
trips.forEach { trip ->
|
|
||||||
stoppingPatternQueries.insert(trip.pattern.asDb())
|
|
||||||
trip.pattern.stoptimes.forEach { stoptime ->
|
|
||||||
stopTimeQueries.insert(stoptime.asDb())
|
|
||||||
}
|
|
||||||
tripQueries.insert(trip.asDb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.info("done")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +1,13 @@
|
||||||
package moe.lava.banksia.server.di
|
package moe.lava.banksia.server.di
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import moe.lava.banksia.core.data.dataDiModule
|
import moe.lava.banksia.server.gtfs.GtfsHandler
|
||||||
import moe.lava.banksia.server.GtfsDataFixer
|
import moe.lava.banksia.server.gtfsr.GtfsrService
|
||||||
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.core.module.dsl.singleOf
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val ServerModules = module {
|
val ServerModules = module {
|
||||||
includes(dataDiModule)
|
|
||||||
|
|
||||||
single { HttpClient() }
|
single { HttpClient() }
|
||||||
singleOf(::GtfsParser)
|
singleOf(::GtfsHandler)
|
||||||
singleOf(::GtfsrtService)
|
singleOf(::GtfsrService)
|
||||||
|
|
||||||
factoryOf(::GtfsDataFixer)
|
|
||||||
factoryOf(::GtfsImporter)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,336 @@
|
||||||
|
package moe.lava.banksia.server.gtfs
|
||||||
|
|
||||||
|
import com.lightningkite.kotlinx.serialization.csv.CsvFormat
|
||||||
|
import com.lightningkite.kotlinx.serialization.csv.StringDeferringConfig
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.request.prepareRequest
|
||||||
|
import io.ktor.client.request.url
|
||||||
|
import io.ktor.client.statement.bodyAsChannel
|
||||||
|
import io.ktor.util.cio.writeChannel
|
||||||
|
import io.ktor.util.logging.Logger
|
||||||
|
import io.ktor.utils.io.copyAndClose
|
||||||
|
import kotlinx.datetime.DayOfWeek
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.modules.EmptySerializersModule
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
import moe.lava.banksia.Constants
|
||||||
|
import moe.lava.banksia.model.Route
|
||||||
|
import moe.lava.banksia.model.Service
|
||||||
|
import moe.lava.banksia.model.Shape
|
||||||
|
import moe.lava.banksia.model.Stop
|
||||||
|
import moe.lava.banksia.model.StopTime
|
||||||
|
import moe.lava.banksia.model.Trip
|
||||||
|
import moe.lava.banksia.room.Database
|
||||||
|
import moe.lava.banksia.room.converter.RouteTypeConverter
|
||||||
|
import moe.lava.banksia.room.entity.asEntity
|
||||||
|
import moe.lava.banksia.server.gtfs.structures.GtfsRoute
|
||||||
|
import moe.lava.banksia.server.gtfs.structures.GtfsService
|
||||||
|
import moe.lava.banksia.server.gtfs.structures.GtfsShape
|
||||||
|
import moe.lava.banksia.server.gtfs.structures.GtfsStop
|
||||||
|
import moe.lava.banksia.server.gtfs.structures.GtfsStopTime
|
||||||
|
import moe.lava.banksia.server.gtfs.structures.GtfsTrip
|
||||||
|
import moe.lava.banksia.util.Point
|
||||||
|
import java.io.File
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
import kotlin.time.Clock
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
|
||||||
|
class GtfsHandler(
|
||||||
|
private val log: Logger,
|
||||||
|
private val client: HttpClient,
|
||||||
|
private val db: Database,
|
||||||
|
) {
|
||||||
|
private val csv = CsvFormat(StringDeferringConfig(EmptySerializersModule()))
|
||||||
|
private val datasetPath = File("/tmp/banksia", "dataset.zip")
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTime::class)
|
||||||
|
suspend fun update(datasetUrl: String, date: Long? = null) {
|
||||||
|
val parentDir = datasetPath.parentFile
|
||||||
|
@Suppress("SimplifyBooleanWithConstants", "KotlinConstantConditions")
|
||||||
|
if (parentDir.exists() && !Constants.devMode)
|
||||||
|
parentDir.deleteRecursively()
|
||||||
|
|
||||||
|
parentDir.mkdirs()
|
||||||
|
|
||||||
|
log.info("fetching..")
|
||||||
|
client.prepareRequest {
|
||||||
|
url(datasetUrl)
|
||||||
|
}.execute { resp ->
|
||||||
|
if (!datasetPath.exists())
|
||||||
|
resp.bodyAsChannel().copyAndClose(datasetPath.writeChannel())
|
||||||
|
log.info("fetched!")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("extracting...")
|
||||||
|
@Suppress("KotlinConstantConditions")
|
||||||
|
val files = if (Constants.devMode) {
|
||||||
|
datasetPath.parentFile
|
||||||
|
.listFiles { it.isDirectory }
|
||||||
|
.flatMap { d -> d.listFiles { f -> f.extension == "txt" }.toList() }
|
||||||
|
.ifEmpty { extractAll(datasetPath) }
|
||||||
|
.filter { it.parentFile.name == "2" }
|
||||||
|
} else {
|
||||||
|
extractAll(datasetPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
addRoutes(files)
|
||||||
|
addStops(files)
|
||||||
|
addShapes(files)
|
||||||
|
val services = addServices(files)
|
||||||
|
val trips = addTrips(files, services.associateBy { it.id })
|
||||||
|
addStopTimes(files, trips.associateBy { it.id })
|
||||||
|
|
||||||
|
updateMetadata(date ?: Clock.System.now().epochSeconds)
|
||||||
|
|
||||||
|
@Suppress("KotlinConstantConditions")
|
||||||
|
if (!Constants.devMode) {
|
||||||
|
parentDir.deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("done!")
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun updateMetadata(date: Long) {
|
||||||
|
val dao = db.versionMetadataDao
|
||||||
|
log.info("updating metadata...")
|
||||||
|
dao.update(date, listOf("routes", "stops", "shapes", "trips", "stop_times"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun addRoutes(files: List<File>) {
|
||||||
|
val dao = db.routeDao
|
||||||
|
log.info("parsing routes...")
|
||||||
|
val routes = files
|
||||||
|
.filter { it.name == "routes.txt" }
|
||||||
|
.flatMap { fd -> parseRoutes(fd) }
|
||||||
|
|
||||||
|
log.info("inserting routes...")
|
||||||
|
dao.deleteAll()
|
||||||
|
dao.insertAll(*routes.map { it.asEntity() }.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseRoutes(fd: File) =
|
||||||
|
fd.parseCsv<GtfsRoute>()
|
||||||
|
.map { with(it) {
|
||||||
|
Route(
|
||||||
|
id = route_id,
|
||||||
|
type = RouteTypeConverter.from(fd.parentFile.name.toInt()),
|
||||||
|
number = route_short_name,
|
||||||
|
name = route_long_name,
|
||||||
|
)
|
||||||
|
} }
|
||||||
|
|
||||||
|
private suspend fun addShapes(files: List<File>) {
|
||||||
|
val dao = db.shapeDao
|
||||||
|
log.info("parsing shapes...")
|
||||||
|
val shapes = files
|
||||||
|
.filter { it.name == "shapes.txt" }
|
||||||
|
.flatMap { fd -> parseShapes(fd) }
|
||||||
|
|
||||||
|
log.info("inserting shapes...")
|
||||||
|
dao.deleteAll()
|
||||||
|
dao.insertAll(*shapes.map { it.asEntity() }.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseShapes(fd: File) =
|
||||||
|
fd.parseCsv<GtfsShape>()
|
||||||
|
.groupBy { it.shape_id }
|
||||||
|
.map { (id, group) ->
|
||||||
|
val points = group
|
||||||
|
.sortedBy { it.shape_pt_sequence }
|
||||||
|
.map { Point(it.shape_pt_lat, it.shape_pt_lon) }
|
||||||
|
|
||||||
|
Shape(id, points)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun addStops(files: List<File>) {
|
||||||
|
val dao = db.stopDao
|
||||||
|
log.info("parsing stops...")
|
||||||
|
val stops = files
|
||||||
|
.filter { it.name == "stops.txt" }
|
||||||
|
.flatMap { fd -> parseStops(fd) }
|
||||||
|
|
||||||
|
log.info("inserting stops...")
|
||||||
|
dao.deleteAll()
|
||||||
|
stops
|
||||||
|
.groupBy { it.id }
|
||||||
|
.forEach { (id, gstops) ->
|
||||||
|
if (gstops.size > 1) {
|
||||||
|
if (gstops.withIndex().any { (i, stop) -> i != 0 && stop != gstops[i - 1] }) {
|
||||||
|
gstops.forEach {
|
||||||
|
log.info("duplicate $id: $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dao.insertOrReplaceAll(*stops.map { it.asEntity() }.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseStops(fd: File) =
|
||||||
|
fd.parseCsv<GtfsStop>()
|
||||||
|
.map { with(it) {
|
||||||
|
Stop(
|
||||||
|
id = stop_id,
|
||||||
|
name = stop_name,
|
||||||
|
pos = Point(stop_lat, stop_lon),
|
||||||
|
parent = parent_station,
|
||||||
|
hasWheelChairBoarding = wheelchair_boarding == "1",
|
||||||
|
level = level_id,
|
||||||
|
platformCode = platform_code,
|
||||||
|
)
|
||||||
|
} }
|
||||||
|
|
||||||
|
private suspend fun addStopTimes(files: List<File>, trips: Map<String, Trip>) {
|
||||||
|
val dao = db.stopTimeDao
|
||||||
|
dao.deleteAll()
|
||||||
|
log.info("parsing stop times...")
|
||||||
|
files
|
||||||
|
.filter { it.name == "stop_times.txt" }
|
||||||
|
.forEach { fd ->
|
||||||
|
log.info("parsing stop times for ${fd.parent}...")
|
||||||
|
parseStopTimes(fd, trips) { seq ->
|
||||||
|
seq.chunked(1000000)
|
||||||
|
.forEach { queue ->
|
||||||
|
log.info("converting stop times (${queue.size}) for ${fd.parent}...")
|
||||||
|
val conv = queue.map { it.asEntity() }.toTypedArray()
|
||||||
|
log.info("inserting stop times (${conv.size}) for ${fd.parent}...")
|
||||||
|
dao.insertOrReplaceAll(*conv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun parseStopTimes(fd: File, trips: Map<String, Trip>, block: (Sequence<StopTime>) -> Unit) =
|
||||||
|
fd.parseCsvSequence<GtfsStopTime> { seq ->
|
||||||
|
seq
|
||||||
|
.map { with(it) {
|
||||||
|
StopTime(
|
||||||
|
tripId = trip_id,
|
||||||
|
stopId = stop_id,
|
||||||
|
arrivalTime = GtfsStopTime.parseGtfsTime(arrival_time),
|
||||||
|
departureTime = GtfsStopTime.parseGtfsTime(departure_time),
|
||||||
|
headsign = stop_headsign.ifEmpty { trips[trip_id]!!.tripHeadsign },
|
||||||
|
pickupType = pickup_type,
|
||||||
|
dropOffType = drop_off_type,
|
||||||
|
)
|
||||||
|
} }
|
||||||
|
.let { block(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun addServices(files: List<File>): List<Service> {
|
||||||
|
val dao = db.serviceDao
|
||||||
|
log.info("parsing services...")
|
||||||
|
val services = files
|
||||||
|
.filter { it.name == "calendar.txt" }
|
||||||
|
.flatMap { fd -> parseServices(fd) }
|
||||||
|
|
||||||
|
log.info("inserting services...")
|
||||||
|
dao.deleteAll()
|
||||||
|
dao.insertOrReplaceAll(*services.map { it.asEntity() }.toTypedArray())
|
||||||
|
|
||||||
|
return services
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseServices(fd: File) =
|
||||||
|
fd.parseCsv<GtfsService>()
|
||||||
|
.map { with(it) {
|
||||||
|
val days = buildList {
|
||||||
|
if (monday == 1) add(DayOfWeek.MONDAY)
|
||||||
|
if (tuesday == 1) add(DayOfWeek.TUESDAY)
|
||||||
|
if (wednesday == 1) add(DayOfWeek.WEDNESDAY)
|
||||||
|
if (thursday == 1) add(DayOfWeek.THURSDAY)
|
||||||
|
if (friday == 1) add(DayOfWeek.FRIDAY)
|
||||||
|
if (saturday == 1) add(DayOfWeek.SATURDAY)
|
||||||
|
if (sunday == 1) add(DayOfWeek.SUNDAY)
|
||||||
|
}
|
||||||
|
Service(
|
||||||
|
id = service_id,
|
||||||
|
days = days,
|
||||||
|
start = LocalDate.parse(start_date, LocalDate.Formats.ISO_BASIC),
|
||||||
|
end = LocalDate.parse(end_date, LocalDate.Formats.ISO_BASIC),
|
||||||
|
)
|
||||||
|
} }
|
||||||
|
|
||||||
|
private suspend fun addTrips(files: List<File>, services: Map<String, Service>): List<Trip> {
|
||||||
|
val dao = db.tripDao
|
||||||
|
log.info("parsing trips...")
|
||||||
|
val trips = files
|
||||||
|
.filter { it.name == "trips.txt" }
|
||||||
|
.flatMap { fd -> parseTrips(fd, services) }
|
||||||
|
|
||||||
|
log.info("inserting trips...")
|
||||||
|
dao.deleteAll()
|
||||||
|
dao.insertOrReplaceAll(*trips.map { it.asEntity() }.toTypedArray())
|
||||||
|
|
||||||
|
return trips
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseTrips(fd: File, services: Map<String, Service>) =
|
||||||
|
fd.parseCsv<GtfsTrip>()
|
||||||
|
.map { with(it) {
|
||||||
|
Trip(
|
||||||
|
id = trip_id,
|
||||||
|
routeId = route_id,
|
||||||
|
service = services[service_id]!!,
|
||||||
|
shapeId = shape_id.ifEmpty { null },
|
||||||
|
tripHeadsign = trip_headsign,
|
||||||
|
directionId = direction_id,
|
||||||
|
blockId = block_id,
|
||||||
|
wheelchairAccessible = wheelchair_accessible,
|
||||||
|
)
|
||||||
|
} }
|
||||||
|
|
||||||
|
private fun extract(fd: File): List<File> {
|
||||||
|
val outputs = mutableListOf<File>()
|
||||||
|
ZipFile(fd).use { zip ->
|
||||||
|
for (entry in zip.entries()) {
|
||||||
|
zip.getInputStream(entry).use { input ->
|
||||||
|
val out = File(fd.parentFile, entry.name)
|
||||||
|
out.parentFile.mkdirs()
|
||||||
|
out.outputStream().use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
outputs.add(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputs
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractAll(fd: File) = extract(fd).flatMap(::extract)
|
||||||
|
|
||||||
|
private inline fun <reified T> File.parseCsv(): List<T> = this
|
||||||
|
.readText()
|
||||||
|
.replace("\uFEFF", "") // remove bom
|
||||||
|
.replace("\r\n", "\n") // crlf -> lf
|
||||||
|
.let { csv.decodeFromString(it) }
|
||||||
|
|
||||||
|
private inline fun <reified T> File.parseCsvSequence(block: (Sequence<T>) -> Unit) = this
|
||||||
|
.bufferedReader()
|
||||||
|
.use { reader ->
|
||||||
|
val iter = object : CharIterator() {
|
||||||
|
var next: Char? = null
|
||||||
|
override fun nextChar(): Char {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw NoSuchElementException()
|
||||||
|
}
|
||||||
|
val ret = next!!
|
||||||
|
next = null
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
if (next == null) {
|
||||||
|
do {
|
||||||
|
next = null
|
||||||
|
val new = reader.read()
|
||||||
|
if (new != -1) {
|
||||||
|
next = new.toChar()
|
||||||
|
}
|
||||||
|
} while (next == '\uFEFF' || next == '\r')
|
||||||
|
}
|
||||||
|
return next != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block(csv.decodeToSequence(iter, csv.serializersModule.serializer()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class GtfsRoute(
|
data class GtfsRoute(
|
||||||
val route_id: String,
|
val route_id: String,
|
||||||
val agency_id: String,
|
val agency_id: String,
|
||||||
val route_short_name: String,
|
val route_short_name: String,
|
||||||
|
|
@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class GtfsService(
|
data class GtfsService(
|
||||||
val service_id: String,
|
val service_id: String,
|
||||||
val monday: Int,
|
val monday: Int,
|
||||||
val tuesday: Int,
|
val tuesday: Int,
|
||||||
|
|
@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class GtfsShape(
|
data class GtfsShape(
|
||||||
val shape_id: String,
|
val shape_id: String,
|
||||||
val shape_pt_lat: Double,
|
val shape_pt_lat: Double,
|
||||||
val shape_pt_lon: Double,
|
val shape_pt_lon: Double,
|
||||||
|
|
@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class GtfsStop(
|
data class GtfsStop(
|
||||||
val stop_id: String,
|
val stop_id: String,
|
||||||
val stop_name: String,
|
val stop_name: String,
|
||||||
val stop_lat: Double,
|
val stop_lat: Double,
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
package moe.lava.banksia.server.gtfs.structures
|
package moe.lava.banksia.server.gtfs.structures
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import moe.lava.banksia.core.model.FutureTime
|
import moe.lava.banksia.model.FutureTime
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class GtfsStopTime(
|
data class GtfsStopTime(
|
||||||
val trip_id: String,
|
val trip_id: String,
|
||||||
val arrival_time: String,
|
val arrival_time: String,
|
||||||
val departure_time: String,
|
val departure_time: String,
|
||||||
val stop_id: String,
|
val stop_id: String,
|
||||||
val stop_sequence: Long,
|
val stop_sequence: Int,
|
||||||
val stop_headsign: String,
|
val stop_headsign: String,
|
||||||
val pickup_type: Int,
|
val pickup_type: Int,
|
||||||
val drop_off_type: Int,
|
val drop_off_type: Int,
|
||||||
|
|
@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class GtfsTrip(
|
data class GtfsTrip(
|
||||||
val route_id: String,
|
val route_id: String,
|
||||||
val service_id: String,
|
val service_id: String,
|
||||||
val trip_id: String,
|
val trip_id: String,
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
package moe.lava.banksia.server.gtfsr
|
||||||
|
|
||||||
|
import com.google.transit.realtime.FeedMessage
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.header
|
||||||
|
import io.ktor.client.request.url
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.client.statement.readRawBytes
|
||||||
|
import io.ktor.http.isSuccess
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.joinAll
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import moe.lava.banksia.Constants
|
||||||
|
import moe.lava.banksia.util.LogScope
|
||||||
|
import moe.lava.banksia.util.log
|
||||||
|
import java.io.File
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.ZoneId
|
||||||
|
|
||||||
|
private const val BASE_DIR = "./data/gtfsr-archive/"
|
||||||
|
|
||||||
|
class GtfsrService(private val client: HttpClient) {
|
||||||
|
private var started = false
|
||||||
|
private val latest = mutableMapOf<String, FeedMessage>()
|
||||||
|
|
||||||
|
fun latestFor(type: String) = latest[type]
|
||||||
|
|
||||||
|
private val iFlow = MutableSharedFlow<Pair<String, FeedMessage>>()
|
||||||
|
val flow = iFlow.asSharedFlow()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val types = arrayOf(
|
||||||
|
"metro/trip-updates",
|
||||||
|
"metro/vehicle-positions",
|
||||||
|
"metro/service-alerts",
|
||||||
|
"tram/trip-updates",
|
||||||
|
"tram/vehicle-positions",
|
||||||
|
"tram/service-alerts",
|
||||||
|
"bus/trip-updates",
|
||||||
|
"bus/vehicle-positions",
|
||||||
|
"vline/trip-updates",
|
||||||
|
"vline/vehicle-positions",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun start() {
|
||||||
|
if (started) {
|
||||||
|
log("GtfsrService", "Tried to start when already started")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
started = true
|
||||||
|
coroutineScope {
|
||||||
|
launch { compressJob() }
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
val results = mutableMapOf<String, ByteArray>()
|
||||||
|
types.map { type ->
|
||||||
|
launch(context = Dispatchers.IO) {
|
||||||
|
val logger = LogScope("gtfsr $type")
|
||||||
|
try {
|
||||||
|
val res = client.get {
|
||||||
|
url("https://api.opendata.transport.vic.gov.au/opendata/public-transport/gtfs/realtime/v1/${type}")
|
||||||
|
header("KeyId", Constants.opendataKey)
|
||||||
|
}
|
||||||
|
if (!res.status.isSuccess()) {
|
||||||
|
logger.log("${res.status} | ${res.bodyAsText()}")
|
||||||
|
} else {
|
||||||
|
results[type] = res.readRawBytes()
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
logger.log("$e")
|
||||||
|
logger.log(e.stackTraceToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.joinAll()
|
||||||
|
|
||||||
|
results.forEach { (type, data) ->
|
||||||
|
val dec = try {
|
||||||
|
FeedMessage.ADAPTER.decode(data)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
log("gtfsr $type", "Failed to parse proto: $e")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
val timestamp = dec.header_.timestamp
|
||||||
|
?: return@forEach log("gtfsr $type", "Failed to read proto timestamp")
|
||||||
|
|
||||||
|
val time = Instant.ofEpochSecond(timestamp).atZone(ZoneId.systemDefault())
|
||||||
|
|
||||||
|
val base = File(BASE_DIR, type)
|
||||||
|
val previousParent = File(base, "${time.year}-${((time.dayOfYear - 1) / 7).toString().padStart(2, '0')}")
|
||||||
|
val currentParent = File(base, "${time.year}-${((time.dayOfYear - 1) / 7 + 1).toString().padStart(2, '0')}")
|
||||||
|
val target = File(currentParent, "${timestamp}.proto")
|
||||||
|
|
||||||
|
if (previousParent.isDirectory) {
|
||||||
|
enqueueCompression(previousParent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target.exists()) {
|
||||||
|
try {
|
||||||
|
if (!target.parentFile.isDirectory) {
|
||||||
|
target.parentFile.mkdirs()
|
||||||
|
}
|
||||||
|
target.writeBytes(data)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
log("gtfsr $type", "Failed to write ${target}: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(10000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cqueue = mutableSetOf<File>()
|
||||||
|
private val ignore = mutableSetOf<File>()
|
||||||
|
private val cmut = Mutex()
|
||||||
|
private suspend fun enqueueCompression(fd: File) {
|
||||||
|
cmut.withLock { cqueue.add(fd) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun compressJob() {
|
||||||
|
while(true) {
|
||||||
|
while(true) {
|
||||||
|
val next = cmut.withLock { cqueue.firstOrNull() }
|
||||||
|
?: break
|
||||||
|
if (!next.isDirectory) {
|
||||||
|
cmut.withLock { cqueue.remove(next) }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (next in ignore) continue
|
||||||
|
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val proc = ProcessBuilder(
|
||||||
|
"tar", "-acf",
|
||||||
|
"${next.absolutePath}.tar.zst",
|
||||||
|
next.absolutePath
|
||||||
|
).start()
|
||||||
|
val exitCode = proc.waitFor()
|
||||||
|
if (exitCode == 0) {
|
||||||
|
if (next.deleteRecursively()) {
|
||||||
|
cmut.withLock { cqueue.remove(next) }
|
||||||
|
} else {
|
||||||
|
log("CompressJob", "Failed to delete $next")
|
||||||
|
ignore.add(next)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val msg = proc.errorStream.readAllBytes().decodeToString()
|
||||||
|
log("CompressJob", "Failed to delete $next (exit code $exitCode")
|
||||||
|
log("CompressJob", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(30000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
<root level="debug">
|
<root level="trace">
|
||||||
<appender-ref ref="FILE"/>
|
<appender-ref ref="FILE"/>
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
||||||
|
|
@ -32,13 +32,9 @@ dependencyResolutionManagement {
|
||||||
}
|
}
|
||||||
|
|
||||||
include(":androidApp")
|
include(":androidApp")
|
||||||
|
include(":client")
|
||||||
include(":server")
|
include(":server")
|
||||||
include(":server:gtfs")
|
include(":shared")
|
||||||
include(":server:gtfs_rt")
|
|
||||||
include(":core")
|
|
||||||
include(":core:data")
|
|
||||||
include(":core:stoptime")
|
|
||||||
include(":core:sqld")
|
|
||||||
include(":ui")
|
include(":ui")
|
||||||
include(":ui:maps")
|
include(":ui:maps")
|
||||||
include(":ui:shared")
|
include(":ui:shared")
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,18 @@ plugins {
|
||||||
alias(libs.plugins.kotlinMultiplatform)
|
alias(libs.plugins.kotlinMultiplatform)
|
||||||
alias(libs.plugins.kotlinSerialization)
|
alias(libs.plugins.kotlinSerialization)
|
||||||
alias(libs.plugins.androidMultiplatformLibrary)
|
alias(libs.plugins.androidMultiplatformLibrary)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
alias(libs.plugins.room)
|
||||||
|
alias(libs.plugins.wire)
|
||||||
|
}
|
||||||
|
|
||||||
|
room {
|
||||||
|
schemaDirectory("$projectDir/schemas")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
android {
|
android {
|
||||||
namespace = "moe.lava.banksia.core"
|
namespace = "moe.lava.banksia.shared"
|
||||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||||
|
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
|
|
@ -40,9 +47,25 @@ kotlin {
|
||||||
implementation(libs.kotlinx.datetime)
|
implementation(libs.kotlinx.datetime)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
implementation(libs.kotlinx.serialization.protobuf)
|
implementation(libs.kotlinx.serialization.protobuf)
|
||||||
|
implementation(libs.room.runtime)
|
||||||
|
implementation(libs.sqlite.bundled)
|
||||||
}
|
}
|
||||||
iosMain.dependencies {
|
iosMain.dependencies {
|
||||||
implementation(libs.ktor.client.darwin)
|
implementation(libs.ktor.client.darwin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
add("kspAndroid", libs.room.compiler)
|
||||||
|
add("kspIosArm64", libs.room.compiler)
|
||||||
|
add("kspIosSimulatorArm64", libs.room.compiler)
|
||||||
|
add("kspJvm", libs.room.compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
wire {
|
||||||
|
sourcePath {
|
||||||
|
srcDir("src/commonMain/proto")
|
||||||
|
}
|
||||||
|
kotlin {}
|
||||||
|
}
|
||||||
72
shared/schemas/moe.lava.banksia.room.Database/1.json
Normal file
72
shared/schemas/moe.lava.banksia.room.Database/1.json
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 1,
|
||||||
|
"identityHash": "e536f5a9b1408377bcc449195169648c",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e536f5a9b1408377bcc449195169648c')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
315
shared/schemas/moe.lava.banksia.room.Database/2.json
Normal file
315
shared/schemas/moe.lava.banksia.room.Database/2.json
Normal file
|
|
@ -0,0 +1,315 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 2,
|
||||||
|
"identityHash": "83ece554400bb035c267dc2414c23293",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Stop",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lat",
|
||||||
|
"columnName": "lat",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lng",
|
||||||
|
"columnName": "lng",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWheelChairBoarding",
|
||||||
|
"columnName": "hasWheelChairBoarding",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformCode",
|
||||||
|
"columnName": "platformCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Stop_parent",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parent"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "StopTime",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "tripId",
|
||||||
|
"columnName": "tripId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "stopId",
|
||||||
|
"columnName": "stopId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrivalTime",
|
||||||
|
"columnName": "arrivalTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departureTime",
|
||||||
|
"columnName": "departureTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headsign",
|
||||||
|
"columnName": "headsign",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pickupType",
|
||||||
|
"columnName": "pickupType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dropOffType",
|
||||||
|
"columnName": "dropOffType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId",
|
||||||
|
"stopId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Trip",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Stop",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Trip",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routeId",
|
||||||
|
"columnName": "routeId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serviceId",
|
||||||
|
"columnName": "serviceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shapeId",
|
||||||
|
"columnName": "shapeId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tripHeadsign",
|
||||||
|
"columnName": "tripHeadsign",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "directionId",
|
||||||
|
"columnName": "directionId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockId",
|
||||||
|
"columnName": "blockId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wheelchairAccessible",
|
||||||
|
"columnName": "wheelchairAccessible",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Trip_routeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Route",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Shape",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '83ece554400bb035c267dc2414c23293')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
339
shared/schemas/moe.lava.banksia.room.Database/3.json
Normal file
339
shared/schemas/moe.lava.banksia.room.Database/3.json
Normal file
|
|
@ -0,0 +1,339 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 3,
|
||||||
|
"identityHash": "5a7252ab3bcae4d0d0950024b19ba002",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Stop",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lat",
|
||||||
|
"columnName": "lat",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lng",
|
||||||
|
"columnName": "lng",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWheelChairBoarding",
|
||||||
|
"columnName": "hasWheelChairBoarding",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformCode",
|
||||||
|
"columnName": "platformCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Stop_parent",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parent"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "StopTime",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "tripId",
|
||||||
|
"columnName": "tripId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "stopId",
|
||||||
|
"columnName": "stopId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrivalTime",
|
||||||
|
"columnName": "arrivalTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departureTime",
|
||||||
|
"columnName": "departureTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headsign",
|
||||||
|
"columnName": "headsign",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pickupType",
|
||||||
|
"columnName": "pickupType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dropOffType",
|
||||||
|
"columnName": "dropOffType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId",
|
||||||
|
"stopId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Trip",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Stop",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Trip",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routeId",
|
||||||
|
"columnName": "routeId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serviceId",
|
||||||
|
"columnName": "serviceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shapeId",
|
||||||
|
"columnName": "shapeId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tripHeadsign",
|
||||||
|
"columnName": "tripHeadsign",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "directionId",
|
||||||
|
"columnName": "directionId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockId",
|
||||||
|
"columnName": "blockId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wheelchairAccessible",
|
||||||
|
"columnName": "wheelchairAccessible",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Trip_routeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Route",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Shape",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "VersionMetadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "lastUpdated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5a7252ab3bcae4d0d0950024b19ba002')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
368
shared/schemas/moe.lava.banksia.room.Database/4.json
Normal file
368
shared/schemas/moe.lava.banksia.room.Database/4.json
Normal file
|
|
@ -0,0 +1,368 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 4,
|
||||||
|
"identityHash": "4426fd2ccc826d9d9d9021546b105850",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Stop",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lat",
|
||||||
|
"columnName": "lat",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lng",
|
||||||
|
"columnName": "lng",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWheelChairBoarding",
|
||||||
|
"columnName": "hasWheelChairBoarding",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformCode",
|
||||||
|
"columnName": "platformCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Stop_parent",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parent"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "StopTime",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "tripId",
|
||||||
|
"columnName": "tripId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "stopId",
|
||||||
|
"columnName": "stopId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrivalTime",
|
||||||
|
"columnName": "arrivalTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departureTime",
|
||||||
|
"columnName": "departureTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headsign",
|
||||||
|
"columnName": "headsign",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pickupType",
|
||||||
|
"columnName": "pickupType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dropOffType",
|
||||||
|
"columnName": "dropOffType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId",
|
||||||
|
"stopId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_tripId",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_stopId",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Trip",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Stop",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Trip",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routeId",
|
||||||
|
"columnName": "routeId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serviceId",
|
||||||
|
"columnName": "serviceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shapeId",
|
||||||
|
"columnName": "shapeId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tripHeadsign",
|
||||||
|
"columnName": "tripHeadsign",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "directionId",
|
||||||
|
"columnName": "directionId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockId",
|
||||||
|
"columnName": "blockId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wheelchairAccessible",
|
||||||
|
"columnName": "wheelchairAccessible",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Trip_shapeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_Trip_routeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Route",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Shape",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "VersionMetadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "lastUpdated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4426fd2ccc826d9d9d9021546b105850')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
368
shared/schemas/moe.lava.banksia.room.Database/5.json
Normal file
368
shared/schemas/moe.lava.banksia.room.Database/5.json
Normal file
|
|
@ -0,0 +1,368 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 5,
|
||||||
|
"identityHash": "4426fd2ccc826d9d9d9021546b105850",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Stop",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lat",
|
||||||
|
"columnName": "lat",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lng",
|
||||||
|
"columnName": "lng",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWheelChairBoarding",
|
||||||
|
"columnName": "hasWheelChairBoarding",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformCode",
|
||||||
|
"columnName": "platformCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Stop_parent",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parent"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "StopTime",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "tripId",
|
||||||
|
"columnName": "tripId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "stopId",
|
||||||
|
"columnName": "stopId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrivalTime",
|
||||||
|
"columnName": "arrivalTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departureTime",
|
||||||
|
"columnName": "departureTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headsign",
|
||||||
|
"columnName": "headsign",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pickupType",
|
||||||
|
"columnName": "pickupType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dropOffType",
|
||||||
|
"columnName": "dropOffType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId",
|
||||||
|
"stopId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_tripId",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_stopId",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Trip",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Stop",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Trip",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routeId",
|
||||||
|
"columnName": "routeId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serviceId",
|
||||||
|
"columnName": "serviceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shapeId",
|
||||||
|
"columnName": "shapeId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tripHeadsign",
|
||||||
|
"columnName": "tripHeadsign",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "directionId",
|
||||||
|
"columnName": "directionId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockId",
|
||||||
|
"columnName": "blockId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wheelchairAccessible",
|
||||||
|
"columnName": "wheelchairAccessible",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Trip_shapeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_Trip_routeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Route",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Shape",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "VersionMetadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "lastUpdated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4426fd2ccc826d9d9d9021546b105850')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
368
shared/schemas/moe.lava.banksia.room.Database/6.json
Normal file
368
shared/schemas/moe.lava.banksia.room.Database/6.json
Normal file
|
|
@ -0,0 +1,368 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 6,
|
||||||
|
"identityHash": "5f52de4cc0ddbcf02a0d8be4cf4d4cfd",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Stop",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lat",
|
||||||
|
"columnName": "lat",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lng",
|
||||||
|
"columnName": "lng",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWheelChairBoarding",
|
||||||
|
"columnName": "hasWheelChairBoarding",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformCode",
|
||||||
|
"columnName": "platformCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Stop_parent",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parent"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "StopTime",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "tripId",
|
||||||
|
"columnName": "tripId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "stopId",
|
||||||
|
"columnName": "stopId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrivalTime",
|
||||||
|
"columnName": "arrivalTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departureTime",
|
||||||
|
"columnName": "departureTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headsign",
|
||||||
|
"columnName": "headsign",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pickupType",
|
||||||
|
"columnName": "pickupType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dropOffType",
|
||||||
|
"columnName": "dropOffType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId",
|
||||||
|
"stopId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_tripId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_stopId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Trip",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Stop",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Trip",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routeId",
|
||||||
|
"columnName": "routeId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serviceId",
|
||||||
|
"columnName": "serviceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shapeId",
|
||||||
|
"columnName": "shapeId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tripHeadsign",
|
||||||
|
"columnName": "tripHeadsign",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "directionId",
|
||||||
|
"columnName": "directionId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockId",
|
||||||
|
"columnName": "blockId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wheelchairAccessible",
|
||||||
|
"columnName": "wheelchairAccessible",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Trip_shapeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_Trip_routeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Route",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Shape",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "VersionMetadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "lastUpdated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5f52de4cc0ddbcf02a0d8be4cf4d4cfd')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
415
shared/schemas/moe.lava.banksia.room.Database/7.json
Normal file
415
shared/schemas/moe.lava.banksia.room.Database/7.json
Normal file
|
|
@ -0,0 +1,415 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 7,
|
||||||
|
"identityHash": "15c94df0a62438ff28d451c074c94c59",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Service",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `days` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "days",
|
||||||
|
"columnName": "days",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "start",
|
||||||
|
"columnName": "start",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "end",
|
||||||
|
"columnName": "end",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Service_days",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"days"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Service_days` ON `${TABLE_NAME}` (`days`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Stop",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lat",
|
||||||
|
"columnName": "lat",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lng",
|
||||||
|
"columnName": "lng",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWheelChairBoarding",
|
||||||
|
"columnName": "hasWheelChairBoarding",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformCode",
|
||||||
|
"columnName": "platformCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Stop_parent",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parent"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "StopTime",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "tripId",
|
||||||
|
"columnName": "tripId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "stopId",
|
||||||
|
"columnName": "stopId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrivalTime",
|
||||||
|
"columnName": "arrivalTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departureTime",
|
||||||
|
"columnName": "departureTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headsign",
|
||||||
|
"columnName": "headsign",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pickupType",
|
||||||
|
"columnName": "pickupType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dropOffType",
|
||||||
|
"columnName": "dropOffType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId",
|
||||||
|
"stopId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_tripId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_stopId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Trip",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Stop",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Trip",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routeId",
|
||||||
|
"columnName": "routeId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serviceId",
|
||||||
|
"columnName": "serviceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shapeId",
|
||||||
|
"columnName": "shapeId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tripHeadsign",
|
||||||
|
"columnName": "tripHeadsign",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "directionId",
|
||||||
|
"columnName": "directionId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockId",
|
||||||
|
"columnName": "blockId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wheelchairAccessible",
|
||||||
|
"columnName": "wheelchairAccessible",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Trip_shapeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_Trip_routeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Route",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Shape",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "VersionMetadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "lastUpdated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '15c94df0a62438ff28d451c074c94c59')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
426
shared/schemas/moe.lava.banksia.room.Database/8.json
Normal file
426
shared/schemas/moe.lava.banksia.room.Database/8.json
Normal file
|
|
@ -0,0 +1,426 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 8,
|
||||||
|
"identityHash": "6e0f07bf1af88b2e37b5ad7c38a3fb2a",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Service",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `days` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "days",
|
||||||
|
"columnName": "days",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "start",
|
||||||
|
"columnName": "start",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "end",
|
||||||
|
"columnName": "end",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Service_days",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"days"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Service_days` ON `${TABLE_NAME}` (`days`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Stop",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lat",
|
||||||
|
"columnName": "lat",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lng",
|
||||||
|
"columnName": "lng",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWheelChairBoarding",
|
||||||
|
"columnName": "hasWheelChairBoarding",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformCode",
|
||||||
|
"columnName": "platformCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Stop_parent",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parent"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "StopTime",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "tripId",
|
||||||
|
"columnName": "tripId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "stopId",
|
||||||
|
"columnName": "stopId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrivalTime",
|
||||||
|
"columnName": "arrivalTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departureTime",
|
||||||
|
"columnName": "departureTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headsign",
|
||||||
|
"columnName": "headsign",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pickupType",
|
||||||
|
"columnName": "pickupType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dropOffType",
|
||||||
|
"columnName": "dropOffType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId",
|
||||||
|
"stopId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_tripId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_stopId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Trip",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Stop",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Trip",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`serviceId`) REFERENCES `Service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routeId",
|
||||||
|
"columnName": "routeId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serviceId",
|
||||||
|
"columnName": "serviceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shapeId",
|
||||||
|
"columnName": "shapeId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tripHeadsign",
|
||||||
|
"columnName": "tripHeadsign",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "directionId",
|
||||||
|
"columnName": "directionId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockId",
|
||||||
|
"columnName": "blockId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wheelchairAccessible",
|
||||||
|
"columnName": "wheelchairAccessible",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Trip_shapeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_Trip_routeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Route",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Service",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"serviceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Shape",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "VersionMetadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "lastUpdated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6e0f07bf1af88b2e37b5ad7c38a3fb2a')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
426
shared/schemas/moe.lava.banksia.room.Database/9.json
Normal file
426
shared/schemas/moe.lava.banksia.room.Database/9.json
Normal file
|
|
@ -0,0 +1,426 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 9,
|
||||||
|
"identityHash": "6e0f07bf1af88b2e37b5ad7c38a3fb2a",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Route",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "number",
|
||||||
|
"columnName": "number",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Service",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `days` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "days",
|
||||||
|
"columnName": "days",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "start",
|
||||||
|
"columnName": "start",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "end",
|
||||||
|
"columnName": "end",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Service_days",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"days"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Service_days` ON `${TABLE_NAME}` (`days`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Shape",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "path",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "BLOB",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Stop",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lat",
|
||||||
|
"columnName": "lat",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lng",
|
||||||
|
"columnName": "lng",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasWheelChairBoarding",
|
||||||
|
"columnName": "hasWheelChairBoarding",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "platformCode",
|
||||||
|
"columnName": "platformCode",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Stop_parent",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parent"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "StopTime",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "tripId",
|
||||||
|
"columnName": "tripId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "stopId",
|
||||||
|
"columnName": "stopId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrivalTime",
|
||||||
|
"columnName": "arrivalTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departureTime",
|
||||||
|
"columnName": "departureTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "headsign",
|
||||||
|
"columnName": "headsign",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pickupType",
|
||||||
|
"columnName": "pickupType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dropOffType",
|
||||||
|
"columnName": "dropOffType",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId",
|
||||||
|
"stopId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_tripId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_StopTime_stopId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Trip",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"tripId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Stop",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"stopId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Trip",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`serviceId`) REFERENCES `Service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "routeId",
|
||||||
|
"columnName": "routeId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "serviceId",
|
||||||
|
"columnName": "serviceId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shapeId",
|
||||||
|
"columnName": "shapeId",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tripHeadsign",
|
||||||
|
"columnName": "tripHeadsign",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "directionId",
|
||||||
|
"columnName": "directionId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "blockId",
|
||||||
|
"columnName": "blockId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wheelchairAccessible",
|
||||||
|
"columnName": "wheelchairAccessible",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Trip_shapeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_Trip_routeId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "Route",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"routeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Service",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"serviceId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "Shape",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"shapeId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "VersionMetadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "lastUpdated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6e0f07bf1af88b2e37b5ad7c38a3fb2a')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package moe.lava.banksia.di
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import moe.lava.banksia.room.Database
|
||||||
|
import org.koin.core.parameter.ParametersHolder
|
||||||
|
import org.koin.core.scope.Scope
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
class AndroidDatabaseBuilder(val ctx: Context) : PlatformDatabaseBuilder {
|
||||||
|
override fun getBuilder(): RoomDatabase.Builder<Database> {
|
||||||
|
val appContext = ctx.applicationContext
|
||||||
|
val dbFile = appContext.getDatabasePath("room.db")
|
||||||
|
return Room.databaseBuilder<Database>(
|
||||||
|
context = appContext,
|
||||||
|
name = dbFile.absolutePath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder =
|
||||||
|
AndroidDatabaseBuilder(get())
|
||||||
|
|
||||||
|
internal actual val ExtPlatformModule = module { }
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package moe.lava.banksia.core.util
|
package moe.lava.banksia.util
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@ object Constants {
|
||||||
const val opendataKey: String = ""
|
const val opendataKey: String = ""
|
||||||
const val serverUrl: String = "https://banksia.lava.moe/api/"
|
const val serverUrl: String = "https://banksia.lava.moe/api/"
|
||||||
// TODO
|
// TODO
|
||||||
var devMode: Boolean = false
|
const val devMode: Boolean = false
|
||||||
const val updateKey: String = ""
|
const val updateKey: String = ""
|
||||||
const val protomapsKey: String = ""
|
const val protomapsKey: String = ""
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package moe.lava.banksia.server.gtfsrt
|
package moe.lava.banksia.data.gtfsr
|
||||||
|
|
||||||
import com.google.transit.realtime.FeedMessage
|
import com.google.transit.realtime.FeedMessage
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package moe.lava.banksia.server.gtfsrt
|
package moe.lava.banksia.data.gtfsr
|
||||||
|
|
||||||
import com.google.transit.realtime.FeedMessage
|
import com.google.transit.realtime.FeedMessage
|
||||||
import moe.lava.banksia.core.util.Point
|
import moe.lava.banksia.util.Point
|
||||||
|
|
||||||
class RealtimeVehiclePositions(data: FeedMessage) : GtfsRealtime(data) {
|
class RealtimeVehiclePositions(data: FeedMessage) : GtfsRealtime(data) {
|
||||||
private val positions = mutableMapOf<String, Point>()
|
private val positions = mutableMapOf<String, Point>()
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue