From 4e1e05495d3deab07d492c7b8052317a047a6dd8 Mon Sep 17 00:00:00 2001 From: Cilly Leang Date: Mon, 13 Apr 2026 03:58:00 +1000 Subject: [PATCH 1/6] featwip: pretty departures --- .../repositories/ClientRouteRepository.kt | 10 + .../sources/route/RouteLocalDataSource.kt | 1 + .../sources/route/RouteRemoteDataSource.kt | 1 + .../core/data/repositories/RouteRepository.kt | 3 +- .../lava/banksia/core/room/dao/RouteDao.kt | 3 + .../moe/lava/banksia/server/Application.kt | 10 + ui/build.gradle.kts | 2 + ui/shared/build.gradle.kts | 5 + .../drawable/arrow_drop_down.xml | 9 + .../drawable/arrow_drop_up.xml | 9 + .../lava/banksia/ui/layout/info/InfoPanel.kt | 3 +- .../banksia/ui/layout/info/StopInfoPanel.kt | 181 ++++++++++++++++-- .../ui/screens/map/MapScreenViewModel.kt | 64 +++++-- 13 files changed, 264 insertions(+), 37 deletions(-) create mode 100644 ui/shared/src/commonMain/composeResources/drawable/arrow_drop_down.xml create mode 100644 ui/shared/src/commonMain/composeResources/drawable/arrow_drop_up.xml diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt index 70a8905..2644785 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt @@ -4,6 +4,7 @@ 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 internal class ClientRouteRepository internal constructor( private val local: RouteLocalDataSource, @@ -21,5 +22,14 @@ internal class ClientRouteRepository internal constructor( } } + private val tripRouteMap = mutableMapOf() + override suspend fun get(id: String) = mutex.withLock { local.get(id)?.asModel() ?: remote.get(id) } + override suspend fun getByTrip(tripId: String) = mutex.withLock { + tripRouteMap[tripId] + ?: remote.getByTrip(tripId).also { + local.save(it) + tripRouteMap[tripId] = it + } + } } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt index ca267c3..e319c80 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt @@ -7,5 +7,6 @@ import moe.lava.banksia.core.room.entity.asEntity internal class RouteLocalDataSource(private val dao: RouteDao) { suspend fun get(id: String) = dao.get(id) suspend fun getAll() = dao.getAll() + suspend fun getByTrip(tripId: String) = dao.getByTrip(tripId) suspend fun save(vararg routes: Route) = dao.insertOrReplaceAll(*routes.map { it.asEntity() }.toTypedArray()) } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt index bdcbfc1..b37bff1 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt @@ -7,5 +7,6 @@ import moe.lava.banksia.core.model.Route internal class RouteRemoteDataSource(val client: HttpClient) { suspend fun get(id: String) = client.get("routes/${id}").body() + suspend fun getByTrip(tripId: String) = client.get("routes/by_trip/${tripId}").body() suspend fun getAll() = client.get("routes").body>() } diff --git a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt index fbb663f..fb302a5 100644 --- a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt +++ b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt @@ -3,6 +3,7 @@ package moe.lava.banksia.core.data.repositories import moe.lava.banksia.core.model.Route interface RouteRepository { - suspend fun get(id: String): Route + suspend fun get(id: String): Route? + suspend fun getByTrip(tripId: String): Route? suspend fun getAll(): List } diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/RouteDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/RouteDao.kt index c791f81..1c9d4df 100644 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/RouteDao.kt +++ b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/RouteDao.kt @@ -28,6 +28,9 @@ interface RouteDao { @Query("DELETE FROM Route") suspend fun deleteAll() + @Query("SELECT * FROM Route INNER JOIN Trip on Route.id == Trip.routeId WHERE Trip.id == :tripId") + suspend fun getByTrip(tripId: String): RouteEntity? + @Query(""" SELECT Stop.* FROM Stop INNER JOIN StopTime ON StopTime.stopId == Stop.id diff --git a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt index 0981b80..9e0c957 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt @@ -118,6 +118,16 @@ fun Application.module() { else call.respond(HttpStatusCode.NotFound) } + get("/routes/by_trip/{trip_id}") { + val tripId = call.parameters["trip_id"]!! + val route = withContext(context = Dispatchers.IO) { + get().getByTrip(tripId) + } + if (route != null) + call.respond(route.asModel()) + else + call.respond(HttpStatusCode.NotFound) + } get("/stops") { val routes = withContext(context = Dispatchers.IO) { get().getAll() diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index 9c5c7bd..871412d 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -41,7 +41,9 @@ kotlin { sourceSets { androidMain.dependencies { + implementation(libs.compose.ui.tooling.preview) implementation(libs.play.services.location) + implementation(projects.ui.shared) } commonMain.dependencies { implementation(libs.compose.components.resources) diff --git a/ui/shared/build.gradle.kts b/ui/shared/build.gradle.kts index e379840..2a78572 100644 --- a/ui/shared/build.gradle.kts +++ b/ui/shared/build.gradle.kts @@ -16,6 +16,10 @@ kotlin { compilerOptions { jvmTarget.set(JvmTarget.JVM_11) } + + androidResources { + enable = true + } } compilerOptions { @@ -47,4 +51,5 @@ dependencies { compose.resources { publicResClass = true packageOfResClass = "moe.lava.banksia.resources" + generateResClass = always } diff --git a/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_down.xml b/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_down.xml new file mode 100644 index 0000000..ac49572 --- /dev/null +++ b/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_up.xml b/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_up.xml new file mode 100644 index 0000000..322fa56 --- /dev/null +++ b/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/InfoPanel.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/InfoPanel.kt index 55eac69..4783998 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/InfoPanel.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/InfoPanel.kt @@ -45,6 +45,7 @@ sealed class InfoPanelState { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun InfoPanel( + modifier: Modifier = Modifier, state: InfoPanelState, onEvent: (InfoPanelEvent) -> Unit, onPeekHeightChange: (Dp) -> Unit, @@ -65,7 +66,7 @@ fun InfoPanel( } Column( - Modifier + modifier = modifier .fillMaxWidth() .padding(horizontal = 24.dp) .onSizeChanged { diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/StopInfoPanel.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/StopInfoPanel.kt index dbe3b29..e7eb04b 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/StopInfoPanel.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/StopInfoPanel.kt @@ -1,21 +1,51 @@ package moe.lava.banksia.ui.layout.info +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Icon +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SegmentedListItem +import androidx.compose.material3.ShapeDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import moe.lava.banksia.resources.Res +import moe.lava.banksia.resources.arrow_drop_down +import moe.lava.banksia.resources.arrow_drop_up +import moe.lava.banksia.ui.extensions.BUS_ORANGE +import moe.lava.banksia.ui.extensions.TRAIN_BLUE +import moe.lava.banksia.ui.platform.BanksiaTheme +import org.jetbrains.compose.resources.painterResource +import kotlin.time.Clock +import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Instant sealed class StopInfoPanelEvent : InfoPanelEvent() @@ -23,19 +53,37 @@ data class StopInfoPanelState( val id: String, val name: String, val subname: String? = null, - val departures: List? = null, + val departures: List? = null, ) : InfoPanelState() { override val loading: Boolean get() = departures == null - data class Departure(val directionName: String, val formattedTimes: String) + data class DeparturePlatforms( + val platform: String, + val departures: List, + ) + + data class DepartureInfo( + val routeName: String, + val routeColour: Color?, + val headsign: String, + val description: String?, + val time: Instant, + ) } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable internal fun StopInfoPanel( state: StopInfoPanelState, onEvent: (StopInfoPanelEvent) -> Unit, ) { + val colors = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + selectedContainerColor = MaterialTheme.colorScheme.primary, + selectedContentColor = MaterialTheme.colorScheme.onPrimary, + ) + Column(Modifier.fillMaxWidth()) { Text( state.name, @@ -53,23 +101,122 @@ internal fun StopInfoPanel( textAlign = TextAlign.Start ) } - state.departures?.let { + state.departures?.let { departurePlatforms -> Spacer(Modifier.height(5.dp)) - it.forEach { (name, formatted) -> - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - name, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - Text( - formatted, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 5.dp) - ) + Column( + modifier = Modifier.verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { + departurePlatforms.forEach { (platform, departures) -> + var expanded by rememberSaveable { mutableStateOf(true) } + val base = ListItemDefaults.segmentedShapes(0, 2) + val large = MaterialTheme.shapes.large + + if (departurePlatforms.size > 1) { + SegmentedListItem( + onClick = { expanded = !expanded }, + colors = colors, + shapes = if (expanded) base else base.copy(shape = large), + trailingContent = { + Icon( + painterResource(if (expanded) Res.drawable.arrow_drop_up else Res.drawable.arrow_drop_down), + contentDescription = null, + modifier = Modifier + .background( + if (expanded) MaterialTheme.colorScheme.surface else Color.Transparent, + shape = RoundedCornerShape(100) + ) + .padding(6.dp), + tint = MaterialTheme.colorScheme.onSurface, + ) + }, + ) { + Text( + text = platform, + style = MaterialTheme.typography.labelLarge, + ) + } + } + AnimatedVisibility( + visible = expanded, + enter = expandVertically(MaterialTheme.motionScheme.fastSpatialSpec()), + exit = shrinkVertically(MaterialTheme.motionScheme.fastSpatialSpec()), + ) { + Column(verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap)) { + departures.forEachIndexed { idx, dep -> + SegmentedListItem( + onClick = {}, +// onClick = { onNavigate(ch) }, + colors = colors, + shapes = ListItemDefaults.segmentedShapes( + idx + 1, + departures.size + 1 + ), + supportingContent = { + dep.description?.let { Text(dep.description) } + }, + trailingContent = { + Text( + text = (dep.time - Clock.System.now()).inWholeMinutes.toString(), + style = MaterialTheme.typography.headlineSmallEmphasized, + ) + }, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + Box( + Modifier + .clip(ShapeDefaults.ExtraSmall) + .background(dep.routeColour ?: MaterialTheme.colorScheme.surface) + .padding(vertical = 2.dp, horizontal = 4.dp) + ) { + Text( + text = dep.routeName, + style = MaterialTheme.typography.labelSmallEmphasized, + color = MaterialTheme.colorScheme.surface, + ) + } + Text( + text = dep.headsign, + style = MaterialTheme.typography.labelLargeEmphasized, + ) + } + } + } + } + } + Spacer(Modifier.height(10.dp)) } } } } } + +@Preview +@Composable +internal fun StopInfoPanelPreview() { + fun dateIn(dur: Duration) = (Clock.System.now() + dur) + + InfoPanel( + modifier = Modifier.background(BanksiaTheme.colors.background), + state = StopInfoPanelState( + id = "id", + name = "name", + subname = "sub", + departures = listOf( + StopInfoPanelState.DeparturePlatforms("Platform 1", listOf( + StopInfoPanelState.DepartureInfo("Sunbury", Color(TRAIN_BLUE), "Sunbury", "··· Malvern -> Anzac ··· Sunbury", dateIn(2.minutes)), + StopInfoPanelState.DepartureInfo("Sunbury", Color(TRAIN_BLUE), "West Footscray", "Express via Metro Tunnel", dateIn(8.minutes)), + )), + StopInfoPanelState.DeparturePlatforms("Platform 2", listOf( + StopInfoPanelState.DepartureInfo("237", Color(BUS_ORANGE), "Westall", null, dateIn(7.minutes)), + StopInfoPanelState.DepartureInfo("442", Color(BUS_ORANGE), "Dandenong", null, dateIn(8.minutes)), + )), + ), + ), + onEvent = {}, + onPeekHeightChange = {}, + ) +} diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt index c4bd768..729630f 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt @@ -26,6 +26,7 @@ import moe.lava.banksia.core.util.LoopFlow.Companion.waitUntilSubscribed import moe.lava.banksia.core.util.Point import moe.lava.banksia.core.util.log import moe.lava.banksia.data.ptv.PtvService +import moe.lava.banksia.ui.extensions.getUIProperties import moe.lava.banksia.ui.layout.info.InfoPanelEvent import moe.lava.banksia.ui.layout.info.InfoPanelState import moe.lava.banksia.ui.layout.info.RouteInfoPanelState @@ -36,8 +37,6 @@ import moe.lava.banksia.ui.map.util.CameraPositionBounds import moe.lava.banksia.ui.map.util.Marker import moe.lava.banksia.ui.state.MapState import moe.lava.banksia.ui.state.SearchState -import kotlin.time.Clock -import kotlin.time.Duration.Companion.minutes sealed class MapScreenEvent { data object DismissState : MapScreenEvent() @@ -165,6 +164,7 @@ class MapScreenViewModel( } val route = routeRepository.get(routeId) + ?: return // val gtfsRoute = ptvService.route(routeId) iInfoState.update { RouteInfoPanelState( @@ -232,23 +232,51 @@ class MapScreenViewModel( } val departures = stopTimeRepository.getForStop(id) - .filter { !it.headsign.isNullOrBlank() } - .groupBy { it.headsign!! } - .map { (headsign, stopTimes) -> - val now = Clock.System.now() - val times = stopTimes - .map { it.arrivalTime.toInstant(TimeZone.currentSystemDefault()) } - .filter { it >= (now - 1.minutes) } - .joinToString(" | ") { - val diff = (it - now).inWholeMinutes.coerceAtLeast(0) - if (diff >= 65) { - "${((diff + 30.0) / 60.0).toInt()}hr" - } else { - "${diff}mn" - } - } - StopInfoPanelState.Departure(headsign, times) + .groupBy { it.stopId } + .mapKeys { (id) -> + val stop = stopRepository.get(id) + if (stop.platformCode.firstOrNull()?.isDigit() == true) { + "Platform " + stop.platformCode + } else { + stop.platformCode + } } + .entries + .sortedBy { (platform) -> platform } + .map { (platform, deps) -> + StopInfoPanelState.DeparturePlatforms( + platform = platform, + departures = deps.take(5).mapNotNull { + val route = routeRepository.getByTrip(it.tripId) + ?: return@mapNotNull null + StopInfoPanelState.DepartureInfo( + routeName = route.number ?: route.name, + routeColour = route.type.getUIProperties().colour, + headsign = it.headsign ?: route.name, + description = null, + time = it.departureTime.toInstant(TimeZone.currentSystemDefault()), + ) + } + ) + } +// val departures = stopTimeRepository.getForStop(id) +// .filter { !it.headsign.isNullOrBlank() } +// .groupBy { it.headsign!! } +// .map { (headsign, stopTimes) -> +// val now = Clock.System.now() +// val times = stopTimes +// .map { it.arrivalTime.toInstant(TimeZone.currentSystemDefault()) } +// .filter { it >= (now - 1.minutes) } +// .joinToString(" | ") { +// val diff = (it - now).inWholeMinutes.coerceAtLeast(0) +// if (diff >= 65) { +// "${((diff + 30.0) / 60.0).toInt()}hr" +// } else { +// "${diff}mn" +// } +// } +// StopInfoPanelState.DeparturePlatforms(headsign, times) +// } iInfoState.update { if (it !is StopInfoPanelState) it From ff2af310fbba3436e6dd2709b23e91beb40a252f Mon Sep 17 00:00:00 2001 From: Cilly Leang Date: Thu, 30 Apr 2026 17:26:41 +1000 Subject: [PATCH 2/6] feat(server): use envvar for devmode constant --- .../kotlin/moe/lava/banksia/core/Constants.kt.skeleton | 2 +- server/src/main/kotlin/moe/lava/banksia/server/Application.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/commonMain/kotlin/moe/lava/banksia/core/Constants.kt.skeleton b/core/src/commonMain/kotlin/moe/lava/banksia/core/Constants.kt.skeleton index 15b3c58..909f642 100644 --- a/core/src/commonMain/kotlin/moe/lava/banksia/core/Constants.kt.skeleton +++ b/core/src/commonMain/kotlin/moe/lava/banksia/core/Constants.kt.skeleton @@ -6,7 +6,7 @@ object Constants { const val opendataKey: String = "" const val serverUrl: String = "https://banksia.lava.moe/api/" // TODO - const val devMode: Boolean = false + var devMode: Boolean = false const val updateKey: String = "" const val protomapsKey: String = "" } diff --git a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt index 0981b80..73e7888 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt @@ -33,11 +33,14 @@ import org.koin.ktor.plugin.Koin import kotlin.time.Clock fun main() { + if (System.getenv("BANKSIA_PRODUCTION") == "1") Constants.devMode = false + embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module) .start(wait = true) } fun Application.module() { + log.info("devMode: ${Constants.devMode}") install(ContentNegotiation) { json() } From f1770744dbd07998f8ccd9fb79c1c4699cc29f47 Mon Sep 17 00:00:00 2001 From: Cilly Leang Date: Sat, 2 May 2026 02:31:18 +1000 Subject: [PATCH 3/6] refactor(core): switch from room to sqldelight sqldelight provides far more control over the sql and allows me to make more optimisations such as removing generated rowid etc. sql also just looks better than the annotation hell from room. --- build.gradle.kts | 1 + core/build.gradle.kts | 15 - core/data/client/build.gradle.kts | 2 +- .../banksia/core/data/ClientDataDiModule.kt | 4 +- .../repositories/ClientRouteRepository.kt | 1 + .../data/repositories/ClientStopRepository.kt | 1 + .../sources/route/RouteLocalDataSource.kt | 23 +- .../data/sources/stop/StopLocalDataSource.kt | 24 +- .../stoptime/StopTimeLocalDataSource.kt | 27 +- core/data/server/build.gradle.kts | 1 - .../1.json | 72 --- .../10.json | 477 ----------------- .../11.json | 498 ------------------ .../2.json | 315 ----------- .../3.json | 339 ------------ .../4.json | 368 ------------- .../5.json | 368 ------------- .../6.json | 368 ------------- .../7.json | 415 --------------- .../8.json | 426 --------------- .../9.json | 426 --------------- .../core/room/DatabaseManager.android.kt | 22 - .../moe/lava/banksia/core/room/Database.kt | 83 --- .../lava/banksia/core/room/RoomDiModule.kt | 18 - .../core/room/converter/RouteTypeConverter.kt | 12 - .../core/room/converter/ShapePathConverter.kt | 43 -- .../lava/banksia/core/room/dao/RouteDao.kt | 58 -- .../lava/banksia/core/room/dao/ServiceDao.kt | 29 - .../core/room/dao/ServiceExceptionDao.kt | 29 - .../lava/banksia/core/room/dao/ShapeDao.kt | 26 - .../moe/lava/banksia/core/room/dao/StopDao.kt | 42 -- .../lava/banksia/core/room/dao/StopTimeDao.kt | 46 -- .../moe/lava/banksia/core/room/dao/TripDao.kt | 32 -- .../core/room/dao/VersionMetadataDao.kt | 27 - .../banksia/core/room/entity/RouteEntity.kt | 18 - .../banksia/core/room/entity/ServiceEntity.kt | 31 -- .../room/entity/ServiceExceptionEntity.kt | 28 - .../banksia/core/room/entity/ShapeEntity.kt | 19 - .../banksia/core/room/entity/StopEntity.kt | 36 -- .../core/room/entity/StopTimeEntity.kt | 53 -- .../banksia/core/room/entity/TripEntity.kt | 49 -- .../core/room/entity/VersionMetadataEntity.kt | 19 - .../banksia/core/room/DatabaseManager.ios.kt | 8 - .../banksia/core/room/DatabaseManager.jvm.kt | 69 --- core/{room => sqld}/build.gradle.kts | 36 +- .../core/sqld/DatabaseManager.android.kt | 14 + .../banksia/core/sqld}/DatabaseManager.kt | 4 +- .../lava/banksia/core/sqld/SqldDiModule.kt | 16 + .../lava/banksia/core/sqld/mappers/Route.kt | 14 + .../lava/banksia/core/sqld/mappers/Service.kt | 21 + .../core/sqld/mappers/ServiceException.kt | 17 + .../lava/banksia/core/sqld/mappers/Shape.kt | 52 ++ .../lava/banksia/core/sqld/mappers/Stop.kt | 26 + .../banksia/core/sqld/mappers/StopTime.kt | 25 + .../lava/banksia/core/sqld/mappers/Trip.kt | 32 ++ .../moe/lava/banksia/core/sqld/Route.sq | 15 + .../moe/lava/banksia/core/sqld/Service.sq | 11 + .../banksia/core/sqld/ServiceException.sq | 9 + .../moe/lava/banksia/core/sqld/Shape.sq | 7 + .../moe/lava/banksia/core/sqld/Stop.sq | 54 ++ .../moe/lava/banksia/core/sqld/StopTime.sq | 23 + .../moe/lava/banksia/core/sqld/Trip.sq | 15 + .../banksia/core/sqld/DatabaseManager.ios.kt | 10 + .../banksia/core/sqld/DatabaseManager.jvm.kt | 58 ++ .../moe/lava/banksia/core/model/Stop.kt | 4 +- .../moe/lava/banksia/core/model/Trip.kt | 6 +- gradle/libs.versions.toml | 11 +- server/build.gradle.kts | 4 +- .../lava/banksia/server/gtfs/GtfsParser.kt | 16 +- .../moe/lava/banksia/server/Application.kt | 60 +-- .../moe/lava/banksia/server/GtfsDataFixer.kt | 22 +- .../moe/lava/banksia/server/GtfsImporter.kt | 82 +-- .../lava/banksia/server/di/ServerModules.kt | 4 +- settings.gradle.kts | 2 +- 74 files changed, 601 insertions(+), 5037 deletions(-) delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/1.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/10.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/11.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/2.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/3.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/4.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/5.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/6.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/7.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/8.json delete mode 100644 core/room/schemas/moe.lava.banksia.core.room.Database/9.json delete mode 100644 core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.android.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/Database.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/converter/RouteTypeConverter.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/converter/ShapePathConverter.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/RouteDao.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ServiceDao.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ServiceExceptionDao.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ShapeDao.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopDao.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopTimeDao.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/TripDao.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/VersionMetadataDao.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/RouteEntity.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ServiceEntity.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ServiceExceptionEntity.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ShapeEntity.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/StopEntity.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/StopTimeEntity.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/TripEntity.kt delete mode 100644 core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/VersionMetadataEntity.kt delete mode 100644 core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.ios.kt delete mode 100644 core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.jvm.kt rename core/{room => sqld}/build.gradle.kts (55%) create mode 100644 core/sqld/src/androidMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.android.kt rename core/{room/src/commonMain/kotlin/moe/lava/banksia/core/room => sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld}/DatabaseManager.kt (58%) create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/SqldDiModule.kt create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Route.kt create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Service.kt create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/ServiceException.kt create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Shape.kt create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Stop.kt create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StopTime.kt create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Trip.kt create mode 100644 core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Route.sq create mode 100644 core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Service.sq create mode 100644 core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/ServiceException.sq create mode 100644 core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Shape.sq create mode 100644 core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Stop.sq create mode 100644 core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq create mode 100644 core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Trip.sq create mode 100644 core/sqld/src/iosMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.ios.kt create mode 100644 core/sqld/src/jvmMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.jvm.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0687328..9434477 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,7 @@ plugins { alias(libs.plugins.composeCompiler) apply false alias(libs.plugins.kotlinJvm) apply false alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.sqldelight) apply false alias(libs.plugins.wire) apply false } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 99f7156..3dd2ee6 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -4,12 +4,6 @@ plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.kotlinSerialization) alias(libs.plugins.androidMultiplatformLibrary) - alias(libs.plugins.ksp) - alias(libs.plugins.room) -} - -room { - schemaDirectory("$projectDir/schemas") } kotlin { @@ -46,18 +40,9 @@ kotlin { implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.serialization.json) implementation(libs.kotlinx.serialization.protobuf) - implementation(libs.room.runtime) - implementation(libs.sqlite.bundled) } iosMain.dependencies { 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) -} diff --git a/core/data/client/build.gradle.kts b/core/data/client/build.gradle.kts index e9848f3..c6d5e5d 100644 --- a/core/data/client/build.gradle.kts +++ b/core/data/client/build.gradle.kts @@ -45,7 +45,7 @@ kotlin { implementation(libs.kotlinx.serialization.protobuf) implementation(projects.core) - implementation(projects.core.room) + implementation(projects.core.sqld) } iosMain.dependencies { implementation(libs.ktor.client.darwin) diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt index 01e961c..2cbfcaa 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt @@ -20,7 +20,7 @@ import moe.lava.banksia.core.data.sources.stop.StopLocalDataSource import moe.lava.banksia.core.data.sources.stop.StopRemoteDataSource import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource import moe.lava.banksia.core.data.sources.stoptime.StopTimeRemoteDataSource -import moe.lava.banksia.core.room.roomDiModule +import moe.lava.banksia.core.sqld.sqldDiModule import moe.lava.banksia.core.util.log import moe.lava.banksia.data.ptv.PtvService import org.koin.core.module.dsl.singleOf @@ -28,7 +28,7 @@ import org.koin.dsl.bind import org.koin.dsl.module val clientDataDiModule = module { - includes(roomDiModule) + includes(sqldDiModule) // HTTP Clients singleOf(::PtvService) diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt index 70a8905..467399a 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt @@ -4,6 +4,7 @@ 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.sqld.mappers.asModel internal class ClientRouteRepository internal constructor( private val local: RouteLocalDataSource, diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt index a5fd300..0aee84e 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt @@ -4,6 +4,7 @@ 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, diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt index ca267c3..8e6af0e 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt @@ -1,11 +1,22 @@ 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.room.dao.RouteDao -import moe.lava.banksia.core.room.entity.asEntity +import moe.lava.banksia.core.sqld.RouteQueries +import moe.lava.banksia.core.sqld.mappers.asDb -internal 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()) +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 save(vararg routes: Route) { + withContext(Dispatchers.IO) { + queries.transaction { + routes.forEach { + queries.insert(it.asDb()) + } + } + } + } } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt index 8e0d8ab..524d123 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt @@ -1,12 +1,22 @@ 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.room.dao.RouteDao -import moe.lava.banksia.core.room.dao.StopDao -import moe.lava.banksia.core.room.entity.asEntity +import moe.lava.banksia.core.sqld.StopQueries +import moe.lava.banksia.core.sqld.mappers.asDb -internal 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()) +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()) + } + } + } + } } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt index c5ce4e7..78ca64b 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt @@ -1,28 +1,35 @@ 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 kotlinx.datetime.TimeZone import kotlinx.datetime.todayIn import moe.lava.banksia.core.model.StopTimeDated import moe.lava.banksia.core.model.atDate -import moe.lava.banksia.core.room.dao.StopTimeDao +import moe.lava.banksia.core.sqld.StopTimeQueries +import moe.lava.banksia.core.sqld.mappers.asModel import moe.lava.banksia.core.util.serialise import kotlin.time.Clock internal class StopTimeLocalDataSource( - private val stopTimeDao: StopTimeDao, + private val queries: StopTimeQueries, ) { suspend fun getAtStop( stopId: String, date: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault()), ): List { - return stopTimeDao - .getForStopDated( - stopId, - listOf(date.dayOfWeek).serialise(), - date.toEpochDays().toInt(), - ) - .map { it.asModel().atDate(date) } - .sortedBy { it.departureTime } + return withContext(Dispatchers.IO) { + queries + .getForStopDated( + listOf(date.dayOfWeek).serialise().toLong(), + date.toEpochDays(), + stopId, + ) + .executeAsList() + .map { it.asModel().atDate(date) } + .sortedBy { it.departureTime } + } } } diff --git a/core/data/server/build.gradle.kts b/core/data/server/build.gradle.kts index eaa309b..d2296d7 100644 --- a/core/data/server/build.gradle.kts +++ b/core/data/server/build.gradle.kts @@ -17,5 +17,4 @@ dependencies { api(projects.core.data) implementation(projects.core) - implementation(projects.core.room) } diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/1.json b/core/room/schemas/moe.lava.banksia.core.room.Database/1.json deleted file mode 100644 index 037062e..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/1.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/10.json b/core/room/schemas/moe.lava.banksia.core.room.Database/10.json deleted file mode 100644 index 751e946..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/10.json +++ /dev/null @@ -1,477 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 10, - "identityHash": "5b90bc800bfae6d22124ea0a6a906ca7", - "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": "ServiceException", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serviceId` TEXT NOT NULL, `date` INTEGER NOT NULL, `type` INTEGER NOT NULL, PRIMARY KEY(`serviceId`, `date`))", - "fields": [ - { - "fieldPath": "serviceId", - "columnName": "serviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "serviceId", - "date" - ] - }, - "indices": [ - { - "name": "index_ServiceException_serviceId", - "unique": false, - "columnNames": [ - "serviceId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_ServiceException_serviceId` ON `${TABLE_NAME}` (`serviceId`)" - }, - { - "name": "index_ServiceException_type", - "unique": false, - "columnNames": [ - "type" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_ServiceException_type` ON `${TABLE_NAME}` (`type`)" - } - ] - }, - { - "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, '5b90bc800bfae6d22124ea0a6a906ca7')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/11.json b/core/room/schemas/moe.lava.banksia.core.room.Database/11.json deleted file mode 100644 index 6fc2976..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/11.json +++ /dev/null @@ -1,498 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 11, - "identityHash": "c4be3d0c2a25f8c5c33132646a070d0e", - "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": "ServiceException", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serviceId` TEXT NOT NULL, `date` INTEGER NOT NULL, `type` INTEGER NOT NULL, PRIMARY KEY(`serviceId`, `date`))", - "fields": [ - { - "fieldPath": "serviceId", - "columnName": "serviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "serviceId", - "date" - ] - }, - "indices": [ - { - "name": "index_ServiceException_serviceId", - "unique": false, - "columnNames": [ - "serviceId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_ServiceException_serviceId` ON `${TABLE_NAME}` (`serviceId`)" - }, - { - "name": "index_ServiceException_type", - "unique": false, - "columnNames": [ - "type" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_ServiceException_type` ON `${TABLE_NAME}` (`type`)" - } - ] - }, - { - "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, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`parent`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED)", - "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" - }, - { - "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`)" - } - ], - "foreignKeys": [ - { - "table": "Stop", - "onDelete": "SET NULL", - "onUpdate": "NO ACTION", - "columns": [ - "parent" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "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_serviceId", - "unique": false, - "columnNames": [ - "serviceId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_serviceId` ON `${TABLE_NAME}` (`serviceId`)" - }, - { - "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, 'c4be3d0c2a25f8c5c33132646a070d0e')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/2.json b/core/room/schemas/moe.lava.banksia.core.room.Database/2.json deleted file mode 100644 index 04a14e3..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/2.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/3.json b/core/room/schemas/moe.lava.banksia.core.room.Database/3.json deleted file mode 100644 index e769926..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/3.json +++ /dev/null @@ -1,339 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/4.json b/core/room/schemas/moe.lava.banksia.core.room.Database/4.json deleted file mode 100644 index 783b3ee..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/4.json +++ /dev/null @@ -1,368 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/5.json b/core/room/schemas/moe.lava.banksia.core.room.Database/5.json deleted file mode 100644 index c4a786d..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/5.json +++ /dev/null @@ -1,368 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/6.json b/core/room/schemas/moe.lava.banksia.core.room.Database/6.json deleted file mode 100644 index 5ab26dc..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/6.json +++ /dev/null @@ -1,368 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/7.json b/core/room/schemas/moe.lava.banksia.core.room.Database/7.json deleted file mode 100644 index d4c62b2..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/7.json +++ /dev/null @@ -1,415 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/8.json b/core/room/schemas/moe.lava.banksia.core.room.Database/8.json deleted file mode 100644 index 9240dd5..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/8.json +++ /dev/null @@ -1,426 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/schemas/moe.lava.banksia.core.room.Database/9.json b/core/room/schemas/moe.lava.banksia.core.room.Database/9.json deleted file mode 100644 index 2359dbd..0000000 --- a/core/room/schemas/moe.lava.banksia.core.room.Database/9.json +++ /dev/null @@ -1,426 +0,0 @@ -{ - "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')" - ] - } -} \ No newline at end of file diff --git a/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.android.kt b/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.android.kt deleted file mode 100644 index 999ee4b..0000000 --- a/core/room/src/androidMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.android.kt +++ /dev/null @@ -1,22 +0,0 @@ -package moe.lava.banksia.core.room - -import android.content.Context -import androidx.room.Room -import org.koin.core.component.KoinComponent -import org.koin.core.component.get -import org.koin.core.component.inject - -actual class DatabaseManager actual constructor() : KoinComponent { - private val ctx by inject() - - actual val database by lazy { - val ctx = get().applicationContext - val dbFile = ctx.getDatabasePath("room.db") - val builder = Room.databaseBuilder( - context = ctx, - name = dbFile.absolutePath, - ) - - Database.build(builder) - } -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/Database.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/Database.kt deleted file mode 100644 index 006b749..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/Database.kt +++ /dev/null @@ -1,83 +0,0 @@ -package moe.lava.banksia.core.room - -import androidx.room.AutoMigration -import androidx.room.RoomDatabase -import androidx.room.TypeConverters -import androidx.room.migration.Migration -import androidx.room.util.foreignKeyCheck -import androidx.sqlite.SQLiteConnection -import androidx.sqlite.driver.bundled.BundledSQLiteDriver -import androidx.sqlite.execSQL -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.IO -import moe.lava.banksia.core.room.converter.RouteTypeConverter -import moe.lava.banksia.core.room.dao.RouteDao -import moe.lava.banksia.core.room.dao.ServiceDao -import moe.lava.banksia.core.room.dao.ServiceExceptionDao -import moe.lava.banksia.core.room.dao.ShapeDao -import moe.lava.banksia.core.room.dao.StopDao -import moe.lava.banksia.core.room.dao.StopTimeDao -import moe.lava.banksia.core.room.dao.TripDao -import moe.lava.banksia.core.room.dao.VersionMetadataDao -import moe.lava.banksia.core.room.entity.RouteEntity -import moe.lava.banksia.core.room.entity.ServiceEntity -import moe.lava.banksia.core.room.entity.ServiceExceptionEntity -import moe.lava.banksia.core.room.entity.ShapeEntity -import moe.lava.banksia.core.room.entity.StopEntity -import moe.lava.banksia.core.room.entity.StopTimeEntity -import moe.lava.banksia.core.room.entity.TripEntity -import moe.lava.banksia.core.room.entity.VersionMetadataEntity -import androidx.room.Database as DatabaseAnnotation - -@DatabaseAnnotation( - version = 11, - entities = [ - RouteEntity::class, - ServiceEntity::class, - ServiceExceptionEntity::class, - ShapeEntity::class, - StopEntity::class, - StopTimeEntity::class, - TripEntity::class, - VersionMetadataEntity::class, - ], - autoMigrations = [ - AutoMigration(from = 1, to = 2), - AutoMigration(from = 2, to = 3), - AutoMigration(from = 9, to = 10), - ] -) -@TypeConverters(RouteTypeConverter::class) -abstract class Database : RoomDatabase() { - abstract val versionMetadataDao: VersionMetadataDao - abstract val routeDao: RouteDao - abstract val serviceDao: ServiceDao - abstract val serviceExceptionDao: ServiceExceptionDao - abstract val shapeDao: ShapeDao - abstract val stopDao: StopDao - abstract val stopTimeDao: StopTimeDao - abstract val tripDao: TripDao - - companion object { - fun build(base: Builder) = - base.fallbackToDestructiveMigration(true) - .setDriver(BundledSQLiteDriver()) - .setQueryCoroutineContext(Dispatchers.IO) - .addMigrations(MIGRATION_10_11) -// .fallbackToDestructiveMigration(true) - .build() - } -} - -val MIGRATION_10_11 = object : Migration(10, 11) { - override fun migrate(connection: SQLiteConnection) { - connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_Stop` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`parent`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED)") - connection.execSQL("INSERT INTO `_new_Stop` (`id`,`name`,`lat`,`lng`,`parent`,`hasWheelChairBoarding`,`level`,`platformCode`) SELECT `id`,`name`,`lat`,`lng`,`parent`,`hasWheelChairBoarding`,`level`,`platformCode` FROM `Stop`") - connection.execSQL("UPDATE `_new_Stop` SET `parent` = NULL WHERE `parent` == \"\"") - connection.execSQL("DROP TABLE `Stop`") - connection.execSQL("ALTER TABLE `_new_Stop` RENAME TO `Stop`") - connection.execSQL("CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `Stop` (`parent`)") - connection.execSQL("CREATE INDEX IF NOT EXISTS `index_Trip_serviceId` ON `Trip` (`serviceId`)") - foreignKeyCheck(connection, "Stop") - } -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.kt deleted file mode 100644 index 85c56fc..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/RoomDiModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package moe.lava.banksia.core.room - -import org.koin.core.module.dsl.singleOf -import org.koin.dsl.module - -val roomDiModule = module { - singleOf(::DatabaseManager) - factory { get().database } - - factory { get().versionMetadataDao } - factory { get().routeDao } - factory { get().serviceDao } - factory { get().serviceExceptionDao } - factory { get().shapeDao } - factory { get().stopDao } - factory { get().stopTimeDao } - factory { get().tripDao } -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/converter/RouteTypeConverter.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/converter/RouteTypeConverter.kt deleted file mode 100644 index f588a66..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/converter/RouteTypeConverter.kt +++ /dev/null @@ -1,12 +0,0 @@ -package moe.lava.banksia.core.room.converter - -import androidx.room.TypeConverter -import moe.lava.banksia.core.model.RouteType - -object RouteTypeConverter { - @TypeConverter - fun from(value: Int) = RouteType.from(value) - - @TypeConverter - fun to(routeType: RouteType) = routeType.value -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/converter/ShapePathConverter.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/converter/ShapePathConverter.kt deleted file mode 100644 index b914cff..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/converter/ShapePathConverter.kt +++ /dev/null @@ -1,43 +0,0 @@ -package moe.lava.banksia.core.room.converter - -import androidx.room.TypeConverter -import moe.lava.banksia.core.model.ShapePath -import moe.lava.banksia.core.util.Point - -object ShapePathConverter { - @TypeConverter - fun from(value: ByteArray): ShapePath { - return value - .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) } - } - - @TypeConverter - fun to(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() - } -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/RouteDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/RouteDao.kt deleted file mode 100644 index c791f81..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/RouteDao.kt +++ /dev/null @@ -1,58 +0,0 @@ -package moe.lava.banksia.core.room.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.Companion.REPLACE -import androidx.room.Query -import moe.lava.banksia.core.room.entity.RouteEntity -import moe.lava.banksia.core.room.entity.StopEntity - -@Dao -interface RouteDao { - @Query("SELECT * FROM Route") - suspend fun getAll(): List - - @Query("SELECT * FROM Route WHERE id == :id") - suspend fun get(id: String): RouteEntity? - - @Insert - suspend fun insertAll(vararg routes: RouteEntity) - - @Insert(onConflict = REPLACE) - suspend fun insertOrReplaceAll(vararg routes: RouteEntity) - - @Delete - suspend fun delete(route: RouteEntity) - - @Query("DELETE FROM Route") - suspend fun deleteAll() - - @Query(""" - SELECT Stop.* FROM Stop - INNER JOIN StopTime ON StopTime.stopId == Stop.id - INNER JOIN Trip ON Trip.id == StopTime.tripId - WHERE Trip.routeId == :id - GROUP BY Stop.id - """) - suspend fun stops(id: String): List - - // I vibecoded this, sorry - @Query(""" - WITH Tree AS ( - SELECT Stop.* FROM Stop - INNER JOIN StopTime ON StopTime.stopId == Stop.id - INNER JOIN Trip ON Trip.id == StopTime.tripId - WHERE Trip.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; - """) - suspend fun stopsParent(id: String): List -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ServiceDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ServiceDao.kt deleted file mode 100644 index e459cdf..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ServiceDao.kt +++ /dev/null @@ -1,29 +0,0 @@ -package moe.lava.banksia.core.room.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.Companion.REPLACE -import androidx.room.Query -import moe.lava.banksia.core.room.entity.ServiceEntity - -@Dao -interface ServiceDao { - @Query("SELECT * FROM Service") - suspend fun getAll(): List - - @Query("SELECT * FROM Service WHERE id == :id") - suspend fun get(id: String): ServiceEntity? - - @Insert - suspend fun insertAll(vararg services: ServiceEntity) - - @Insert(onConflict = REPLACE) - suspend fun insertOrReplaceAll(vararg services: ServiceEntity) - - @Delete - suspend fun delete(service: ServiceEntity) - - @Query("DELETE FROM Service") - suspend fun deleteAll() -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ServiceExceptionDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ServiceExceptionDao.kt deleted file mode 100644 index 86feb75..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ServiceExceptionDao.kt +++ /dev/null @@ -1,29 +0,0 @@ -package moe.lava.banksia.core.room.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.Companion.REPLACE -import androidx.room.Query -import moe.lava.banksia.core.room.entity.ServiceExceptionEntity - -@Dao -interface ServiceExceptionDao { - @Query("SELECT * FROM ServiceException") - suspend fun getAll(): List - - @Query("SELECT * FROM ServiceException WHERE serviceId == :id") - suspend fun get(id: String): List - - @Insert - suspend fun insertAll(vararg exceptions: ServiceExceptionEntity) - - @Insert(onConflict = REPLACE) - suspend fun insertOrReplaceAll(vararg exceptions: ServiceExceptionEntity) - - @Delete - suspend fun delete(service: ServiceExceptionEntity) - - @Query("DELETE FROM ServiceException") - suspend fun deleteAll() -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ShapeDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ShapeDao.kt deleted file mode 100644 index 446a923..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/ShapeDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package moe.lava.banksia.core.room.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.Companion.REPLACE -import androidx.room.Query -import moe.lava.banksia.core.room.entity.ShapeEntity - -@Dao -interface ShapeDao { - @Query("SELECT * FROM Shape WHERE id == :id") - suspend fun get(id: String): ShapeEntity? - - @Insert - suspend fun insertAll(vararg shapes: ShapeEntity) - - @Insert(onConflict = REPLACE) - suspend fun insertOrReplaceAll(vararg shapes: ShapeEntity) - - @Delete - suspend fun delete(shape: ShapeEntity) - - @Query("DELETE FROM Shape") - suspend fun deleteAll() -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopDao.kt deleted file mode 100644 index 7edb560..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopDao.kt +++ /dev/null @@ -1,42 +0,0 @@ -package moe.lava.banksia.core.room.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.Companion.REPLACE -import androidx.room.Query -import moe.lava.banksia.core.room.entity.StopEntity - -@Dao -interface StopDao { - @Query("SELECT * FROM Stop") - suspend fun getAll(): List - - @Query(""" - SELECT * FROM Stop - WHERE platformCode <> "" - AND parent IS NULL - """) - suspend fun getAllParentless(): List - - @Query("SELECT * FROM Stop WHERE id == :id") - suspend fun get(id: String): StopEntity? - - @Query("SELECT * FROM Stop WHERE id IN (:ids)") - suspend fun get(ids: List): List - - @Insert - suspend fun insertAll(vararg stops: StopEntity) - - @Insert(onConflict = REPLACE) - suspend fun insertOrReplaceAll(vararg stops: StopEntity) - - @Delete - suspend fun delete(stop: StopEntity) - - @Query("DELETE FROM Stop") - suspend fun deleteAll() - - @Query("UPDATE Stop SET parent = :parent WHERE id IN (:ids)") - suspend fun updateParents(ids: List, parent: String) -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopTimeDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopTimeDao.kt deleted file mode 100644 index 4670b6e..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/StopTimeDao.kt +++ /dev/null @@ -1,46 +0,0 @@ -package moe.lava.banksia.core.room.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.Companion.REPLACE -import androidx.room.Query -import moe.lava.banksia.core.room.entity.StopTimeEntity - -@Dao -interface StopTimeDao { - @Query("SELECT * FROM StopTime") - suspend fun getAll(): List - - @Query("SELECT * FROM StopTime WHERE tripId == :tripId") - suspend fun getForTrip(tripId: String): StopTimeEntity? - - @Query("SELECT * FROM StopTime WHERE tripId IN (:tripIds)") - suspend fun getForTrips(tripIds: List): List - - @Query("SELECT * FROM StopTime WHERE stopId == :stopId") - suspend fun getForStop(stopId: String): List - - @Query(""" - SELECT DISTINCT StopTime.* FROM StopTime - INNER JOIN Service ON Service.days & :days = :days AND :date BETWEEN Service.start AND Service.`end` - INNER JOIN Trip ON Trip.serviceId == Service.id - LEFT JOIN ServiceException ON ServiceException.serviceId == Service.id AND ServiceException.date == :date - WHERE StopTime.tripId == Trip.id - AND StopTime.stopId IN (SELECT Stop.id FROM Stop WHERE Stop.parent == :stopId OR Stop.id == :stopId) - AND ServiceException.type IS NULL - """) - suspend fun getForStopDated(stopId: String, days: Int, date: Int): List - - @Insert - suspend fun insertAll(vararg stopTimes: StopTimeEntity) - - @Insert(onConflict = REPLACE) - suspend fun insertOrReplaceAll(vararg stopTimes: StopTimeEntity) - - @Delete - suspend fun delete(stopTime: StopTimeEntity) - - @Query("DELETE FROM StopTime") - suspend fun deleteAll() -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/TripDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/TripDao.kt deleted file mode 100644 index 1798f61..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/TripDao.kt +++ /dev/null @@ -1,32 +0,0 @@ -package moe.lava.banksia.core.room.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.Companion.REPLACE -import androidx.room.Query -import moe.lava.banksia.core.room.entity.TripEntity - -@Dao -interface TripDao { - @Query("SELECT * FROM Trip") - suspend fun getAll(): List - - @Query("SELECT * FROM Trip WHERE id == :id") - suspend fun get(id: String): TripEntity? - - @Query("SELECT * FROM Trip WHERE routeId == :id") - suspend fun getByRoute(id: String): List - - @Insert - suspend fun insertAll(vararg trips: TripEntity) - - @Insert(onConflict = REPLACE) - suspend fun insertOrReplaceAll(vararg trips: TripEntity) - - @Delete - suspend fun delete(trip: TripEntity) - - @Query("DELETE FROM Trip") - suspend fun deleteAll() -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/VersionMetadataDao.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/VersionMetadataDao.kt deleted file mode 100644 index 357770d..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/dao/VersionMetadataDao.kt +++ /dev/null @@ -1,27 +0,0 @@ -package moe.lava.banksia.core.room.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy.Companion.REPLACE -import androidx.room.Query -import moe.lava.banksia.core.room.entity.VersionMetadataEntity - -@Dao -interface VersionMetadataDao { - @Query("SELECT * FROM VersionMetadata WHERE type == :type") - suspend fun get(type: String): VersionMetadataEntity? - - @Query("SELECT * FROM VersionMetadata") - suspend fun getAll(): List - - @Insert(onConflict = REPLACE) - suspend fun update(vararg data: VersionMetadataEntity) - - suspend fun update(vararg data: Pair) { - update(*data.map { (type, lastUpdated) -> VersionMetadataEntity(type, lastUpdated) }.toTypedArray()) - } - - suspend fun update(lastUpdated: Long, types: Collection) { - update(*types.map { VersionMetadataEntity(it, lastUpdated) }.toTypedArray()) - } -} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/RouteEntity.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/RouteEntity.kt deleted file mode 100644 index 8feda0b..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/RouteEntity.kt +++ /dev/null @@ -1,18 +0,0 @@ -package moe.lava.banksia.core.room.entity - -import androidx.room.Entity -import androidx.room.PrimaryKey -import moe.lava.banksia.core.model.Route -import moe.lava.banksia.core.model.RouteType - -@Entity("Route") -data class RouteEntity( - @PrimaryKey val id: String, - val type: RouteType, - val number: String?, - val name: String, -) { - fun asModel() = Route(id, type, number, name) -} - -fun Route.asEntity() = RouteEntity(id, type, number, name) diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ServiceEntity.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ServiceEntity.kt deleted file mode 100644 index 4a1c8b7..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ServiceEntity.kt +++ /dev/null @@ -1,31 +0,0 @@ -package moe.lava.banksia.core.room.entity - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -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 - -@Entity("Service") -data class ServiceEntity( - @PrimaryKey val id: String, - @ColumnInfo(index = true) val days: Int, - val start: Int, - val end: Int, -) { - fun asModel() = Service( - id, - days.deserialiseDaysBitflag(), - LocalDate.fromEpochDays(start), - LocalDate.fromEpochDays(end), - ) -} - -fun Service.asEntity() = ServiceEntity( - id, - days.serialise(), - start.toEpochDays().toInt(), - end.toEpochDays().toInt(), -) diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ServiceExceptionEntity.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ServiceExceptionEntity.kt deleted file mode 100644 index 1ac45d3..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ServiceExceptionEntity.kt +++ /dev/null @@ -1,28 +0,0 @@ -package moe.lava.banksia.core.room.entity - -import androidx.room.ColumnInfo -import androidx.room.Entity -import kotlinx.datetime.LocalDate -import moe.lava.banksia.core.model.ServiceException - -@Entity( - "ServiceException", - primaryKeys = ["serviceId", "date"] -) -data class ServiceExceptionEntity( - @ColumnInfo(index = true) val serviceId: String, - val date: Int, - @ColumnInfo(index = true) val type: Int, -) { - fun asModel() = ServiceException( - serviceId, - LocalDate.fromEpochDays(date), - type, - ) -} - -fun ServiceException.asEntity() = ServiceExceptionEntity( - serviceId, - date.toEpochDays().toInt(), - type, -) diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ShapeEntity.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ShapeEntity.kt deleted file mode 100644 index a19147d..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/ShapeEntity.kt +++ /dev/null @@ -1,19 +0,0 @@ -package moe.lava.banksia.core.room.entity - -import androidx.room.Entity -import androidx.room.PrimaryKey -import androidx.room.TypeConverters -import moe.lava.banksia.core.model.Shape -import moe.lava.banksia.core.model.ShapePath -import moe.lava.banksia.core.room.converter.ShapePathConverter - -@Entity("Shape") -@TypeConverters(ShapePathConverter::class) -data class ShapeEntity( - @PrimaryKey val id: String, - val path: ShapePath, -) { - fun asModel() = Shape(id, path) -} - -fun Shape.asEntity() = ShapeEntity(id, path) diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/StopEntity.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/StopEntity.kt deleted file mode 100644 index f59c5da..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/StopEntity.kt +++ /dev/null @@ -1,36 +0,0 @@ -package moe.lava.banksia.core.room.entity - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.ForeignKey.Companion.SET_NULL -import androidx.room.PrimaryKey -import moe.lava.banksia.core.model.Stop -import moe.lava.banksia.core.util.Point - -@Entity( - "Stop", - foreignKeys = [ - ForeignKey( - StopEntity::class, - parentColumns = ["id"], - childColumns = ["parent"], - onDelete = SET_NULL, - deferred = true, - ), - ] -) -data class StopEntity( - @PrimaryKey val id: String, - val name: String, - val lat: Double, - val lng: Double, - @ColumnInfo(index = true) val parent: String?, - val hasWheelChairBoarding: Boolean, - val level: String, - val platformCode: String, -) { - fun asModel() = Stop(id, name, Point(lat, lng), parent, hasWheelChairBoarding, level, platformCode) -} - -fun Stop.asEntity() = StopEntity(id, name, pos.lat, pos.lng, parent, hasWheelChairBoarding, level, platformCode) diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/StopTimeEntity.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/StopTimeEntity.kt deleted file mode 100644 index d96036d..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/StopTimeEntity.kt +++ /dev/null @@ -1,53 +0,0 @@ -package moe.lava.banksia.core.room.entity - -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.ForeignKey.Companion.CASCADE -import androidx.room.Index -import kotlinx.serialization.ExperimentalSerializationApi -import moe.lava.banksia.core.model.FutureTime -import moe.lava.banksia.core.model.FutureTime.Companion.asInt -import moe.lava.banksia.core.model.StopTime - -@Entity( - "StopTime", - primaryKeys = ["tripId", "stopId"], - indices = [ - Index("tripId", unique = false), - Index("stopId", unique = false), - ], - foreignKeys = [ - ForeignKey(TripEntity::class, parentColumns = ["id"], childColumns = ["tripId"], onDelete = CASCADE), - ForeignKey(StopEntity::class, parentColumns = ["id"], childColumns = ["stopId"], onDelete = CASCADE), - ] -) -data class StopTimeEntity( - val tripId: String, - val stopId: String, - val arrivalTime: Int, - val departureTime: Int, - val headsign: String?, - val pickupType: Int, - val dropOffType: Int, -) { - fun asModel() = StopTime( - tripId, - stopId, - FutureTime.fromInt(arrivalTime), - FutureTime.fromInt(departureTime), - headsign, - pickupType, - dropOffType, - ) -} - -@OptIn(ExperimentalSerializationApi::class) -fun StopTime.asEntity() = StopTimeEntity( - tripId, - stopId, - arrivalTime.asInt(), - departureTime.asInt(), - headsign, - pickupType, - dropOffType, -) diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/TripEntity.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/TripEntity.kt deleted file mode 100644 index 7928c60..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/TripEntity.kt +++ /dev/null @@ -1,49 +0,0 @@ -package moe.lava.banksia.core.room.entity - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.ForeignKey.Companion.CASCADE -import androidx.room.Index -import androidx.room.PrimaryKey -import moe.lava.banksia.core.model.Trip - -@Entity( - "Trip", - foreignKeys = [ - ForeignKey(RouteEntity::class, parentColumns = ["id"], childColumns = ["routeId"], onDelete = CASCADE), - ForeignKey(ServiceEntity::class, parentColumns = ["id"], childColumns = ["serviceId"], onDelete = CASCADE), - ForeignKey(ShapeEntity::class, parentColumns = ["id"], childColumns = ["shapeId"], onDelete = CASCADE), - ], - indices = [Index("shapeId"), Index("serviceId")], -) -data class TripEntity( - @PrimaryKey val id: String, - @ColumnInfo(index = true) val routeId: String, - val serviceId: String, - val shapeId: String?, - val tripHeadsign: String, - val directionId: String, - val blockId: String, - val wheelchairAccessible: String, -) - -fun Trip.Companion.from(tripEntity: TripEntity, serviceEntity: ServiceEntity): Trip { - if (tripEntity.serviceId != serviceEntity.id) { - throw IllegalArgumentException("trip and service id mismatch (${tripEntity.serviceId} != ${serviceEntity.id})") - } - return with(tripEntity) { - Trip( - id = id, - routeId = routeId, - service = serviceEntity.asModel(), - shapeId = shapeId, - tripHeadsign = tripHeadsign, - directionId = directionId, - blockId = blockId, - wheelchairAccessible = wheelchairAccessible - ) - } -} - -fun Trip.asEntity() = TripEntity(id, routeId, service.id, shapeId, tripHeadsign, directionId, blockId, wheelchairAccessible) diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/VersionMetadataEntity.kt b/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/VersionMetadataEntity.kt deleted file mode 100644 index 1e7cab9..0000000 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/entity/VersionMetadataEntity.kt +++ /dev/null @@ -1,19 +0,0 @@ -package moe.lava.banksia.core.room.entity - -import androidx.room.Entity -import androidx.room.PrimaryKey -import moe.lava.banksia.core.model.VersionMetadata - -@Entity( - "VersionMetadata", -) -data class VersionMetadataEntity( - /** Entity type this metadata applies to */ - @PrimaryKey val type: String, - /** Last updated */ - val lastUpdated: Long, -) { - fun asModel() = VersionMetadata(type, lastUpdated) -} - -fun VersionMetadata.asEntity() = VersionMetadataEntity(type, lastUpdated) diff --git a/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.ios.kt b/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.ios.kt deleted file mode 100644 index 34e370e..0000000 --- a/core/room/src/iosMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.ios.kt +++ /dev/null @@ -1,8 +0,0 @@ -package moe.lava.banksia.core.room - -import org.koin.core.component.KoinComponent - -actual class DatabaseManager actual constructor() : KoinComponent { - actual val database: Database - get() = TODO("Not yet implemented") -} diff --git a/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.jvm.kt b/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.jvm.kt deleted file mode 100644 index 360d2dd..0000000 --- a/core/room/src/jvmMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.jvm.kt +++ /dev/null @@ -1,69 +0,0 @@ -package moe.lava.banksia.core.room - -import androidx.room.Room -import androidx.room.RoomDatabase -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import moe.lava.banksia.core.util.error -import org.koin.core.component.KoinComponent -import java.io.File -import kotlin.system.exitProcess - -actual class DatabaseManager : KoinComponent { - private var liveDatabase: Database = Database.build(getBuilder()) - actual val database get() = liveDatabase - - private fun getBuilder(path: String = "./data/room.db"): RoomDatabase.Builder { - val dbFile = File(path) - return Room.databaseBuilder( - name = dbFile.absolutePath, - ).setJournalMode(RoomDatabase.JournalMode.TRUNCATE) - } - - fun makeAlt() = Database.build(getBuilder("./data/room_alt.db")) - - private fun deleteAll(file: File): Boolean { - val r1 = file.takeIf { it.exists() }?.delete() - val r2 = File(file.parentFile, file.name + ".lck").takeIf { it.exists() }?.delete() - val r3 = File(file.parentFile, file.name + "-journal").takeIf { it.exists() }?.delete() - return r1 != false && r2 != false && r3 != false - } - - private fun renameAll(from: File, to: File): Boolean { - val r1 = from.takeIf { it.exists() }?.renameTo(to) - val r2 = File(from.parentFile, from.name + ".lck").takeIf { it.exists() }?.renameTo(File(to.parentFile, to.name + ".lck")) - val r3 = File(from.parentFile, from.name + "-journal").takeIf { it.exists() }?.renameTo(File(to.parentFile, to.name + "-journal")) - return r1 != false && r2 != false && r3 != false - } - - fun swap(scope: CoroutineScope = CoroutineScope(Dispatchers.IO)) { - val live = File("./data/room.db") - val alt = File("./data/room_alt.db") - val old = File("./data/room_old.db") - - if (!renameAll(live, old)) { - error("DatabaseManager", "Failed to rename database from live to old (${live.absolutePath} -> ${old.absolutePath})") - return - } - if (!renameAll(alt, live)) { - error("DatabaseManager", "Failed to rename database from alt to live, trying to undo.. (${alt.absolutePath} -> ${live.absolutePath})") - if (!live.renameTo(old)) { - error("DatabaseManager", "Failed to undo, critical failure, exiting..") - exitProcess(1) - } - return - } - val oldDatabase = liveDatabase - liveDatabase = Database.build(getBuilder()) - - scope.launch { - delay(5000) - if (!deleteAll(old)) { - error("DatabaseManager", "Failed to unlink old database, stray files! (${old.absolutePath})") - } - oldDatabase.close() - } - } -} diff --git a/core/room/build.gradle.kts b/core/sqld/build.gradle.kts similarity index 55% rename from core/room/build.gradle.kts rename to core/sqld/build.gradle.kts index 31a7393..d86247a 100644 --- a/core/room/build.gradle.kts +++ b/core/sqld/build.gradle.kts @@ -4,17 +4,12 @@ plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.kotlinSerialization) alias(libs.plugins.androidMultiplatformLibrary) - alias(libs.plugins.ksp) - alias(libs.plugins.room) -} - -room { - schemaDirectory("$projectDir/schemas") + alias(libs.plugins.sqldelight) } kotlin { android { - namespace = "moe.lava.banksia.core.room" + namespace = "moe.lava.banksia.core.sqld" compileSdk = libs.versions.android.compileSdk.get().toInt() compilerOptions { @@ -22,33 +17,36 @@ kotlin { } } - compilerOptions { - freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime") - } - iosArm64() iosSimulatorArm64() jvm() sourceSets { + androidMain.dependencies { + implementation(libs.sqldelight.driver.android) + } commonMain.dependencies { implementation(libs.okio) implementation(libs.koin.core) implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.datetime) - implementation(libs.kotlinx.serialization.json) - implementation(libs.room.runtime) - implementation(libs.sqlite.bundled) implementation(projects.core) } + nativeMain.dependencies { + implementation(libs.sqldelight.driver.native) + } + jvmMain.dependencies { + implementation(libs.sqldelight.driver.jvm) + } } } -dependencies { - add("kspAndroid", libs.room.compiler) - add("kspIosArm64", libs.room.compiler) - add("kspIosSimulatorArm64", libs.room.compiler) - add("kspJvm", libs.room.compiler) +sqldelight { + databases { + register("BanksiaDatabase") { + packageName.set("moe.lava.banksia.core.sqld") + } + } } diff --git a/core/sqld/src/androidMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.android.kt b/core/sqld/src/androidMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.android.kt new file mode 100644 index 0000000..b9f2247 --- /dev/null +++ b/core/sqld/src/androidMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.android.kt @@ -0,0 +1,14 @@ +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 actual constructor() : KoinComponent { + actual val database by lazy { + val ctx = get().applicationContext + val driver = AndroidSqliteDriver(BanksiaDatabase.Schema, ctx, "timetable.db") + BanksiaDatabase(driver) + } +} diff --git a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.kt similarity index 58% rename from core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.kt rename to core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.kt index bfe32a9..c6b29f1 100644 --- a/core/room/src/commonMain/kotlin/moe/lava/banksia/core/room/DatabaseManager.kt +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.kt @@ -1,7 +1,7 @@ -package moe.lava.banksia.core.room +package moe.lava.banksia.core.sqld import org.koin.core.component.KoinComponent expect class DatabaseManager() : KoinComponent { - val database: Database + val database: BanksiaDatabase } diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/SqldDiModule.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/SqldDiModule.kt new file mode 100644 index 0000000..24ab9bd --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/SqldDiModule.kt @@ -0,0 +1,16 @@ +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().database } + factory { get().routeQueries } + factory { get().serviceQueries } + factory { get().serviceExceptionQueries } + factory { get().shapeQueries } + factory { get().stopQueries } + factory { get().stopTimeQueries } + factory { get().tripQueries } +} diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Route.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Route.kt new file mode 100644 index 0000000..f3a5521 --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Route.kt @@ -0,0 +1,14 @@ +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) diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Service.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Service.kt new file mode 100644 index 0000000..dbda5ea --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Service.kt @@ -0,0 +1,21 @@ +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(), +) diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/ServiceException.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/ServiceException.kt new file mode 100644 index 0000000..ef0d201 --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/ServiceException.kt @@ -0,0 +1,17 @@ +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(), +) diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Shape.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Shape.kt new file mode 100644 index 0000000..4a8d7db --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Shape.kt @@ -0,0 +1,52 @@ +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() +} diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Stop.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Stop.kt new file mode 100644 index 0000000..3bf6b54 --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Stop.kt @@ -0,0 +1,26 @@ +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 +) diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StopTime.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StopTime.kt new file mode 100644 index 0000000..c8b1c44 --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StopTime.kt @@ -0,0 +1,25 @@ +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.sqld.StopTime as DbStopTime + +fun DbStopTime.asModel() = StopTime( + tripId = tripId, + stopId = stopId, + arrivalTime = FutureTime.fromInt(arrivalTime.toInt()), + departureTime = FutureTime.fromInt(departureTime.toInt()), + headsign = null, + pickupType = pickupType.toInt(), + dropOffType = dropOffType.toInt(), +) + +fun StopTime.asDb() = DbStopTime( + tripId = tripId, + stopId = stopId, + arrivalTime = arrivalTime.asInt().toLong(), + departureTime = departureTime.asInt().toLong(), + pickupType = pickupType.toLong(), + dropOffType = dropOffType.toLong(), +) diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Trip.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Trip.kt new file mode 100644 index 0000000..36cdad5 --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Trip.kt @@ -0,0 +1,32 @@ +package moe.lava.banksia.core.sqld.mappers + +import moe.lava.banksia.core.model.Service +import moe.lava.banksia.core.model.Trip +import moe.lava.banksia.core.sqld.Trip as DbTrip + +fun DbTrip.asModel(service: Service): Trip { + if (serviceId != service.id) { + throw IllegalArgumentException("trip and service id mismatch (${serviceId} != ${service.id})") + } + return Trip( + id = id, + routeId = routeId, + service = service, + shapeId = shapeId, + tripHeadsign = tripHeadsign, + directionId = directionId, + blockId = blockId, + wheelchairAccessible = wheelchairAccessible == 1L + ) +} + +fun Trip.asDb() = DbTrip( + id = id, + routeId = routeId, + serviceId = service.id, + shapeId = shapeId, + tripHeadsign = tripHeadsign, + directionId = directionId, + blockId = blockId, + wheelchairAccessible = if (wheelchairAccessible) 1L else 0L +) diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Route.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Route.sq new file mode 100644 index 0000000..f1617f7 --- /dev/null +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Route.sq @@ -0,0 +1,15 @@ +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 == ?; + +insert: +INSERT INTO Route VALUES ?; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Service.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Service.sq new file mode 100644 index 0000000..a1c5fad --- /dev/null +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Service.sq @@ -0,0 +1,11 @@ +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 ?; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/ServiceException.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/ServiceException.sq new file mode 100644 index 0000000..332f198 --- /dev/null +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/ServiceException.sq @@ -0,0 +1,9 @@ +CREATE TABLE ServiceException ( + serviceId TEXT NOT NULL, + type INTEGER NOT NULL, + date INTEGER NOT NULL, + PRIMARY KEY (serviceId, type) +); + +insert: +INSERT INTO ServiceException VALUES ?; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Shape.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Shape.sq new file mode 100644 index 0000000..8734200 --- /dev/null +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Shape.sq @@ -0,0 +1,7 @@ +CREATE TABLE Shape ( + id TEXT PRIMARY KEY NOT NULL, + path BLOB NOT NULL +); + +insert: +INSERT INTO Shape VALUES ?; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Stop.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Stop.sq new file mode 100644 index 0000000..3ac06b9 --- /dev/null +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Stop.sq @@ -0,0 +1,54 @@ +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 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 Trip ON Trip.id == StopTime.tripId +WHERE Trip.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 Trip ON Trip.id == StopTime.tripId + WHERE Trip.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; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq new file mode 100644 index 0000000..f2dc824 --- /dev/null +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq @@ -0,0 +1,23 @@ +CREATE TABLE StopTime ( + tripId TEXT NOT NULL REFERENCES Trip (id), + stopId TEXT NOT NULL REFERENCES Stop (id), + arrivalTime INTEGER NOT NULL, + departureTime INTEGER NOT NULL, + pickupType INTEGER NOT NULL, + dropOffType INTEGER NOT NULL, + PRIMARY KEY (tripId, 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` +INNER JOIN Trip ON Trip.serviceId == Service.id +LEFT JOIN ServiceException ON ServiceException.serviceId == Service.id AND ServiceException.date == :date +WHERE StopTime.tripId == Trip.id + AND StopTime.stopId IN (SELECT Stop.id FROM Stop WHERE Stop.parent == :stopId OR Stop.id == :stopId) + AND ServiceException.type IS NULL; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Trip.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Trip.sq new file mode 100644 index 0000000..0fd3c1f --- /dev/null +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Trip.sq @@ -0,0 +1,15 @@ +CREATE TABLE Trip ( + id TEXT PRIMARY KEY NOT NULL, + routeId TEXT NOT NULL REFERENCES Route (id), + serviceId TEXT NOT NULL REFERENCES Service (id), + shapeId TEXT NOT NULL REFERENCES Shape (id), + tripHeadsign TEXT NOT NULL, + directionId TEXT NOT NULL, + blockId TEXT, + wheelchairAccessible INTEGER NOT NULL +); + +CREATE INDEX idx_Trip_serviceId ON Trip (serviceId); + +insert: +INSERT OR REPLACE INTO Trip VALUES ?; diff --git a/core/sqld/src/iosMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.ios.kt b/core/sqld/src/iosMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.ios.kt new file mode 100644 index 0000000..b91b786 --- /dev/null +++ b/core/sqld/src/iosMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.ios.kt @@ -0,0 +1,10 @@ +package moe.lava.banksia.core.sqld + +import app.cash.sqldelight.driver.native.NativeSqliteDriver + +actual class DatabaseManager actual constructor() : org.koin.core.component.KoinComponent { + actual val database by lazy { + val driver = NativeSqliteDriver(BanksiaDatabase.Schema, "timetable.db") + BanksiaDatabase(driver) + } +} diff --git a/core/sqld/src/jvmMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.jvm.kt b/core/sqld/src/jvmMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.jvm.kt new file mode 100644 index 0000000..5bf9f5c --- /dev/null +++ b/core/sqld/src/jvmMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.jvm.kt @@ -0,0 +1,58 @@ +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 + +private const val DBNAME = "timetable" + +actual class DatabaseManager actual constructor() : 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() + } + } +} diff --git a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Stop.kt b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Stop.kt index 73e6f02..bbe6fbf 100644 --- a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Stop.kt +++ b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Stop.kt @@ -10,6 +10,6 @@ data class Stop( val pos: Point, val parent: String?, val hasWheelChairBoarding: Boolean, - val level: String, - val platformCode: String, + val level: String?, + val platformCode: String?, ) diff --git a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Trip.kt b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Trip.kt index 753f653..6edb538 100644 --- a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Trip.kt +++ b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Trip.kt @@ -7,9 +7,9 @@ data class Trip( val id: String, val routeId: String, val service: Service, - val shapeId: String?, + val shapeId: String, val tripHeadsign: String, val directionId: String, - val blockId: String, - val wheelchairAccessible: String, + val blockId: String?, + val wheelchairAccessible: Boolean, ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e171b55..02ca8cc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,9 +22,8 @@ material = "1.7.3" material3 = "1.11.0-alpha04" okio = "3.17.0" playServicesLocation = "21.3.0" -sqlite = "2.6.2" -room = "2.8.4" secretsGradlePlugin = "2.0.1" +sqldelight = "2.3.2" wire = "6.1.0" [libraries] @@ -67,10 +66,10 @@ 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" } play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "playServicesLocation" } -room-compiler = { group = "androidx.room", name = "room-compiler", 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" } +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" } [plugins] @@ -83,6 +82,6 @@ kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } 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" } +sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } wire = { id = "com.squareup.wire", version.ref = "wire" } diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 4150bb2..93da532 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -20,7 +20,7 @@ kotlin { dependencies { implementation(projects.core) - implementation(projects.core.room) + implementation(projects.core.sqld) implementation(projects.server.gtfs) implementation(projects.server.gtfsRt) @@ -36,8 +36,6 @@ dependencies { implementation(libs.ktor.server.contentnegotiation) implementation(libs.ktor.server.core) implementation(libs.ktor.server.netty) - implementation(libs.room.runtime) - implementation(libs.sqlite.bundled) testImplementation(libs.ktor.server.tests) testImplementation(libs.kotlin.test.junit) } diff --git a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt index f8d9832..1a5ec29 100644 --- a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt +++ b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt @@ -175,8 +175,8 @@ class GtfsParser( pos = Point(stop_lat, stop_lon), parent = parent_station.ifEmpty { null }, hasWheelChairBoarding = wheelchair_boarding == "1", - level = level_id, - platformCode = platform_code, + level = level_id.ifEmpty { null }, + platformCode = platform_code.ifEmpty { null }, ) } } @@ -210,7 +210,7 @@ class GtfsParser( if (sunday == 1) add(DayOfWeek.SUNDAY) } Service( - id = service_id, + 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), @@ -221,7 +221,7 @@ class GtfsParser( fd.parseCsv() .map { with(it) { ServiceException( - serviceId = service_id, + serviceId = "${fd.parentFile.name}_${service_id}", date = LocalDate.parse(date, LocalDate.Formats.ISO_BASIC), type = exception_type, ) @@ -233,12 +233,12 @@ class GtfsParser( Trip( id = trip_id, routeId = route_id, - service = services[service_id]!!, - shapeId = shape_id.ifEmpty { null }, + service = services["${fd.parentFile.name}_${service_id}"]!!, + shapeId = shape_id, tripHeadsign = trip_headsign, directionId = direction_id, - blockId = block_id, - wheelchairAccessible = wheelchair_accessible, + blockId = block_id.ifEmpty { null }, + wheelchairAccessible = wheelchair_accessible == "1", ) } } diff --git a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt index 73e7888..9ffd03a 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt @@ -20,10 +20,10 @@ import kotlinx.datetime.TimeZone import kotlinx.datetime.todayIn import moe.lava.banksia.core.Constants import moe.lava.banksia.core.model.atDate -import moe.lava.banksia.core.room.dao.RouteDao -import moe.lava.banksia.core.room.dao.StopDao -import moe.lava.banksia.core.room.dao.StopTimeDao -import moe.lava.banksia.core.room.dao.VersionMetadataDao +import moe.lava.banksia.core.sqld.RouteQueries +import moe.lava.banksia.core.sqld.StopQueries +import moe.lava.banksia.core.sqld.StopTimeQueries +import moe.lava.banksia.core.sqld.mappers.asModel import moe.lava.banksia.core.util.serialise import moe.lava.banksia.server.di.ServerModules import moe.lava.banksia.server.gtfsrt.GtfsrtService @@ -88,25 +88,9 @@ fun Application.module() { } } - get("/metadata/{type?}") { - val dao = get() - 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") { val routes = withContext(context = Dispatchers.IO) { - get().getAll() + get().getAll().executeAsList() } val res = routes.map { it.asModel() } call.respond(res) @@ -114,16 +98,17 @@ fun Application.module() { get("/routes/{route_id}") { val routeId = call.parameters["route_id"]!! val route = withContext(context = Dispatchers.IO) { - get().get(routeId) + get().get(routeId).executeAsOneOrNull() } - if (route != null) + if (route != null) { call.respond(route.asModel()) - else + } else { call.respond(HttpStatusCode.NotFound) + } } get("/stops") { val routes = withContext(context = Dispatchers.IO) { - get().getAll() + get().getAll().executeAsList() } val res = routes.map { it.asModel() } call.respond(res) @@ -131,22 +116,24 @@ fun Application.module() { get("/stops/{stop_id}") { val stopId = call.parameters["stop_id"]!! val stop = withContext(context = Dispatchers.IO) { - get().get(stopId) + get().get(stopId).executeAsOneOrNull() } - if (stop != null) + if (stop != null) { call.respond(stop.asModel()) - else + } else { call.respond(HttpStatusCode.NotFound) + } } get("/route_stops/{route_id}") { val routeId = call.parameters["route_id"]!! val useParent = call.queryParameters["parent"] !in listOf("false", "0") val stops = withContext(Dispatchers.IO) { - val routeDao = get() - if (useParent) - routeDao.stopsParent(routeId) - else - routeDao.stops(routeId) + val queries = get() + if (useParent) { + queries.getParentsByRoute(routeId).executeAsList() + } else { + queries.getByRoute(routeId).executeAsList() + } } call.respond(stops.map { it.asModel() }) } @@ -156,12 +143,13 @@ fun Application.module() { ?.let { LocalDate.parse(it, LocalDate.Formats.ISO) } ?: Clock.System.todayIn(TimeZone.currentSystemDefault()) val times = withContext(context = Dispatchers.IO) { - get() + get() .getForStopDated( + listOf(date.dayOfWeek).serialise().toLong(), + date.toEpochDays(), stopId, - listOf(date.dayOfWeek).serialise(), - date.toEpochDays().toInt(), ) + .executeAsList() .map { it.asModel().atDate(date) } .sortedBy { it.departureTime } } diff --git a/server/src/main/kotlin/moe/lava/banksia/server/GtfsDataFixer.kt b/server/src/main/kotlin/moe/lava/banksia/server/GtfsDataFixer.kt index c74930d..97892e0 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/GtfsDataFixer.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/GtfsDataFixer.kt @@ -1,16 +1,16 @@ package moe.lava.banksia.server -import moe.lava.banksia.core.room.Database -import moe.lava.banksia.core.room.entity.StopEntity +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: Database, + private val database: BanksiaDatabase, ) { - suspend fun addParentsToStops() { - val dao = database.stopDao - val stops = dao.getAllParentless() + fun addParentsToStops() { + val queries = database.stopQueries + val stops = queries.getAllParentless().executeAsList() stops .groupBy { it.name.split("/")[0] } .filter { (_, stops) -> stops.size > 1 } @@ -19,19 +19,21 @@ class GtfsDataFixer( val avgLng = stops.map { it.lng }.average() val hash = name.sha256().substring(0, 7) val parentId = "bsia:df1:$hash" - val parent = StopEntity( + val parent = DbStop( id = parentId, name = name, lat = avgLat, lng = avgLng, parent = null, - hasWheelChairBoarding = stops.all { it.hasWheelChairBoarding }, + hasWheelChairBoarding = if (stops.all { it.hasWheelChairBoarding == 1L }) 1L else 0L, level = "", platformCode = "", ) log("datafixer", "inserting ${parentId} for ${stops.size} children") - dao.insertAll(parent) - dao.updateParents(stops.map { it.id }, parentId) + queries.transaction { + queries.insert(parent) + queries.updateParents(parentId, stops.map { it.id }) + } } } } diff --git a/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt b/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt index 5c8dc37..17ab1f5 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt @@ -8,12 +8,12 @@ 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.Trip -import moe.lava.banksia.core.room.Database -import moe.lava.banksia.core.room.DatabaseManager -import moe.lava.banksia.core.room.entity.asEntity +import moe.lava.banksia.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, @@ -21,7 +21,7 @@ class GtfsImporter( private val log: Logger, ) { suspend fun import(url: String, date: Long = Clock.System.now().epochSeconds) { - val database = dbm.makeAlt() + val (database, close) = dbm.makeAlt() parser.update(url).collect { chunk -> when (chunk) { @@ -35,48 +35,51 @@ class GtfsImporter( } } - database.updateMetadata(date) - database.close() + close() dbm.swap() } - private suspend fun Database.updateMetadata(date: Long) { - val dao = versionMetadataDao - log.info("updating metadata...") - dao.update(date, listOf("routes", "stops", "shapes", "trips", "stop_times")) - log.info("done") - } - - private suspend fun Database.addRoutes(routes: List) { - val dao = routeDao + private fun Database.addRoutes(routes: List) { log.info("inserting routes...") - dao.insertOrReplaceAll(*routes.map { it.asEntity() }.toTypedArray()) + routeQueries.transaction { + routes.forEach { + routeQueries.insert(it.asDb()) + } + } log.info("done") } - private suspend fun Database.addServices(services: List) { - val dao = serviceDao + private fun Database.addServices(services: List) { log.info("inserting services...") - dao.insertOrReplaceAll(*services.map { it.asEntity() }.toTypedArray()) + serviceQueries.transaction { + services.forEach { + serviceQueries.insert(it.asDb()) + } + } log.info("done") } - private suspend fun Database.addServiceExceptions(exceptions: List) { - val dao = serviceExceptionDao + private fun Database.addServiceExceptions(exceptions: List) { log.info("inserting exceptions...") - dao.insertOrReplaceAll(*exceptions.map { it.asEntity() }.toTypedArray()) + serviceExceptionQueries.transaction { + exceptions.forEach { + serviceExceptionQueries.insert(it.asDb()) + } + } log.info("done") } - private suspend fun Database.addShapes(shapes: List) { - val dao = shapeDao + private fun Database.addShapes(shapes: List) { log.info("inserting shapes...") - dao.insertOrReplaceAll(*shapes.map { it.asEntity() }.toTypedArray()) + shapeQueries.transaction { + shapes.forEach { + shapeQueries.insert(it.asDb()) + } + } log.info("done") } - private suspend fun Database.addStops(stops: List) { - val dao = stopDao + private fun Database.addStops(stops: List) { log.info("inserting stops...") stops .groupBy { it.id } @@ -89,21 +92,32 @@ class GtfsImporter( } } } - dao.insertOrReplaceAll(*stops.map { it.asEntity() }.toTypedArray()) + + stopQueries.transaction { + stops.forEach { + stopQueries.insert(it.asDb()) + } + } log.info("done") } - private suspend fun Database.addStopTimes(stopTimes: List) { - val dao = stopTimeDao + private fun Database.addStopTimes(stopTimes: List) { log.info("inserting ${stopTimes.size} stoptimes...") - dao.insertOrReplaceAll(*stopTimes.map { it.asEntity() }.toTypedArray()) + stopTimeQueries.transaction { + stopTimes.forEach { + stopTimeQueries.insert(it.asDb()) + } + } log.info("done") } - private suspend fun Database.addTrips(trips: List) { - val dao = tripDao + private fun Database.addTrips(trips: List) { log.info("inserting ${trips.size} trips...") - dao.insertOrReplaceAll(*trips.map { it.asEntity() }.toTypedArray()) + tripQueries.transaction { + trips.forEach { + tripQueries.insert(it.asDb()) + } + } log.info("done") } } diff --git a/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt b/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt index a8a7541..881becf 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt @@ -1,7 +1,7 @@ package moe.lava.banksia.server.di import io.ktor.client.HttpClient -import moe.lava.banksia.core.room.roomDiModule +import moe.lava.banksia.core.sqld.sqldDiModule import moe.lava.banksia.server.GtfsDataFixer import moe.lava.banksia.server.GtfsImporter import moe.lava.banksia.server.gtfs.GtfsParser @@ -11,7 +11,7 @@ import org.koin.core.module.dsl.singleOf import org.koin.dsl.module val ServerModules = module { - includes(roomDiModule) + includes(sqldDiModule) single { HttpClient() } singleOf(::GtfsParser) diff --git a/settings.gradle.kts b/settings.gradle.kts index 9abcf7f..78831ff 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -39,7 +39,7 @@ include(":core") include(":core:data") include(":core:data:client") include(":core:data:server") -include(":core:room") +include(":core:sqld") include(":ui") include(":ui:maps") include(":ui:shared") From 102c02840724fb99560ce076d808714dbf1048e1 Mon Sep 17 00:00:00 2001 From: Cilly Leang Date: Tue, 5 May 2026 03:23:11 +1000 Subject: [PATCH 4/6] refactor: optimisation around stoptimes - moved stoptime related functionality into new core:data:stoptime module - will feature all the different realtime stoptime sources to be integrated later - create proper database schema for future migrations - deduplicate trips into stoppingpatterns, since many trips share the exact same stopping pattern - stoptimes are now linked to stoppingpatterns instead - stoppingpattern ids are generated from a hash composed of all stoptimes - stoptimes now use deltas for arrival time to save space --- core/data/build.gradle.kts | 1 + .../banksia/core/data/ClientDataDiModule.kt | 8 +- .../repositories/ClientStopTimeRepository.kt | 16 ---- .../data/sources/trip/TripRemoteDataSource.kt | 18 ---- .../data/repositories/StopTimeRepository.kt | 7 -- core/data/stoptime/build.gradle.kts | 60 +++++++++++++ .../core/data/StopTimeDataDiModule.client.kt | 11 +++ .../repositories/StopTimeRepository.client.kt | 19 ++++ .../stoptime/StopTimeRemoteDataSource.kt | 18 +--- .../banksia/core/data/StopTimeDataDiModule.kt | 13 +++ .../data/repositories/StopTimeRepository.kt | 15 ++++ .../stoptime/StopTimeLocalDataSource.kt | 22 ++--- .../core/data/StopTimeDataDiModule.jvm.kt | 9 ++ .../repositories/StopTimeRepository.jvm.kt | 13 +++ core/sqld/build.gradle.kts | 1 + .../core/sqld/DatabaseManager.android.kt | 4 +- .../lava/banksia/core/sqld/DatabaseManager.kt | 4 +- .../lava/banksia/core/sqld/SqldDiModule.kt | 1 + .../banksia/core/sqld/mappers/StopTime.kt | 18 ++-- .../core/sqld/mappers/StoppingPattern.kt | 22 +++++ .../lava/banksia/core/sqld/mappers/Trip.kt | 27 +++--- .../moe/lava/banksia/core/sqld/Stop.sq | 10 +-- .../moe/lava/banksia/core/sqld/StopTime.sq | 11 +-- .../lava/banksia/core/sqld/StoppingPattern.sq | 10 +++ .../moe/lava/banksia/core/sqld/Trip.sq | 12 ++- .../src/commonMain/sqldelight/schema/1.db | Bin 0 -> 81920 bytes .../banksia/core/sqld/DatabaseManager.ios.kt | 5 +- .../banksia/core/sqld/DatabaseManager.jvm.kt | 4 +- .../moe/lava/banksia/core/model/StopTime.kt | 39 ++++++-- .../lava/banksia/core/model/StopTimeDated.kt | 26 ------ .../banksia/core/model/StoppingPattern.kt | 16 ++++ .../moe/lava/banksia/core/model/Trip.kt | 14 +-- .../lava/banksia/server/gtfs/GtfsParser.kt | 84 ++++++++++++++---- .../server/gtfs/structures/GtfsStopTime.kt | 2 +- .../moe/lava/banksia/server/Application.kt | 2 +- .../moe/lava/banksia/server/GtfsImporter.kt | 24 ++--- server/src/main/resources/logback.xml | 2 +- settings.gradle.kts | 1 + .../ui/screens/map/MapScreenViewModel.kt | 50 ++++++----- 39 files changed, 396 insertions(+), 223 deletions(-) delete mode 100644 core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopTimeRepository.kt delete mode 100644 core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/trip/TripRemoteDataSource.kt delete mode 100644 core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt create mode 100644 core/data/stoptime/build.gradle.kts create mode 100644 core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt create mode 100644 core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt rename core/data/{client/src/commonMain => stoptime/src/clientMain}/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt (54%) create mode 100644 core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt create mode 100644 core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt rename core/data/{client => stoptime}/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt (58%) create mode 100644 core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt create mode 100644 core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt create mode 100644 core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StoppingPattern.kt create mode 100644 core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StoppingPattern.sq create mode 100644 core/sqld/src/commonMain/sqldelight/schema/1.db delete mode 100644 core/src/commonMain/kotlin/moe/lava/banksia/core/model/StopTimeDated.kt create mode 100644 core/src/commonMain/kotlin/moe/lava/banksia/core/model/StoppingPattern.kt diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 6f78b5c..8c89aff 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -27,6 +27,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(projects.core) + api(projects.core.data.stoptime) } } } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt index 2cbfcaa..0384f88 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt +++ b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt @@ -10,16 +10,12 @@ import kotlinx.serialization.json.Json import moe.lava.banksia.core.Constants import moe.lava.banksia.core.data.repositories.ClientRouteRepository import moe.lava.banksia.core.data.repositories.ClientStopRepository -import moe.lava.banksia.core.data.repositories.ClientStopTimeRepository import moe.lava.banksia.core.data.repositories.RouteRepository import moe.lava.banksia.core.data.repositories.StopRepository -import moe.lava.banksia.core.data.repositories.StopTimeRepository import moe.lava.banksia.core.data.sources.route.RouteLocalDataSource import moe.lava.banksia.core.data.sources.route.RouteRemoteDataSource import moe.lava.banksia.core.data.sources.stop.StopLocalDataSource import moe.lava.banksia.core.data.sources.stop.StopRemoteDataSource -import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource -import moe.lava.banksia.core.data.sources.stoptime.StopTimeRemoteDataSource import moe.lava.banksia.core.sqld.sqldDiModule import moe.lava.banksia.core.util.log import moe.lava.banksia.data.ptv.PtvService @@ -29,6 +25,7 @@ import org.koin.dsl.module val clientDataDiModule = module { includes(sqldDiModule) + includes(stopTimeDataDiModule) // HTTP Clients singleOf(::PtvService) @@ -56,11 +53,8 @@ val clientDataDiModule = module { singleOf(::RouteRemoteDataSource) singleOf(::StopLocalDataSource) singleOf(::StopRemoteDataSource) - singleOf(::StopTimeLocalDataSource) - singleOf(::StopTimeRemoteDataSource) // Repositories singleOf(::ClientRouteRepository) bind RouteRepository::class singleOf(::ClientStopRepository) bind StopRepository::class - singleOf(::ClientStopTimeRepository) bind StopTimeRepository::class } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopTimeRepository.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopTimeRepository.kt deleted file mode 100644 index aea3159..0000000 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopTimeRepository.kt +++ /dev/null @@ -1,16 +0,0 @@ -package moe.lava.banksia.core.data.repositories - -import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource -import moe.lava.banksia.core.data.sources.stoptime.StopTimeRemoteDataSource -import moe.lava.banksia.core.model.StopTimeDated - -internal class ClientStopTimeRepository internal constructor( - private val local: StopTimeLocalDataSource, - private val remote: StopTimeRemoteDataSource, -) : StopTimeRepository { - override suspend fun getForStop(id: String): List { - return local - .getAtStop(id) - .ifEmpty { remote.getAtStop(id) } - } -} diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/trip/TripRemoteDataSource.kt b/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/trip/TripRemoteDataSource.kt deleted file mode 100644 index d1067d8..0000000 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/trip/TripRemoteDataSource.kt +++ /dev/null @@ -1,18 +0,0 @@ -package moe.lava.banksia.core.data.sources.trip - -import io.ktor.client.HttpClient -import kotlinx.datetime.DayOfWeek -import kotlinx.datetime.TimeZone -import kotlinx.datetime.todayIn -import moe.lava.banksia.core.model.Trip -import kotlin.time.Clock - -internal class TripRemoteDataSource( - private val client: HttpClient, -) { - suspend fun get( - day: DayOfWeek? = Clock.System.todayIn(TimeZone.currentSystemDefault()).dayOfWeek, - ): List { - return listOf() - } -} diff --git a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt deleted file mode 100644 index 87d01ac..0000000 --- a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt +++ /dev/null @@ -1,7 +0,0 @@ -package moe.lava.banksia.core.data.repositories - -import moe.lava.banksia.core.model.StopTimeDated - -interface StopTimeRepository { - suspend fun getForStop(id: String): List -} diff --git a/core/data/stoptime/build.gradle.kts b/core/data/stoptime/build.gradle.kts new file mode 100644 index 0000000..086e749 --- /dev/null +++ b/core/data/stoptime/build.gradle.kts @@ -0,0 +1,60 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.kotlinSerialization) + alias(libs.plugins.androidMultiplatformLibrary) + alias(libs.plugins.ksp) +} + +kotlin { + android { + namespace = "moe.lava.banksia.core.data.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) + } + } +} diff --git a/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt b/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt new file mode 100644 index 0000000..2f83304 --- /dev/null +++ b/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt @@ -0,0 +1,11 @@ +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) +} diff --git a/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt b/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt new file mode 100644 index 0000000..ecaff8e --- /dev/null +++ b/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt @@ -0,0 +1,19 @@ +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) } + } +} diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt b/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt similarity index 54% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt rename to core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt index 0633a18..1d338ce 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt +++ b/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt @@ -7,7 +7,7 @@ import io.ktor.client.request.parameter import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.todayIn -import moe.lava.banksia.core.model.StopTimeDated +import moe.lava.banksia.core.model.StopTime import kotlin.time.Clock internal class StopTimeRemoteDataSource( @@ -16,21 +16,9 @@ internal class StopTimeRemoteDataSource( suspend fun getAtStop( stopId: String, date: LocalDate? = Clock.System.todayIn(TimeZone.currentSystemDefault()), - ): List { + ): List { return client.get("stoptimes/by_stop/${stopId}") { parameter("date", date) - }.body>() + }.body>() } - - /*suspend fun get( - stop: String? = null, - trip: String? = null, - day: DayOfWeek? = Clock.System.todayIn(TimeZone.currentSystemDefault()).dayOfWeek, - ): List { - return client.get("stoptimes") { - stop?.let { parameter("stop", it) } - trip?.let { parameter("trip", it) } - day?.let { parameter("day", it) } - }.body>() - }*/ } diff --git a/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt b/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt new file mode 100644 index 0000000..d46affa --- /dev/null +++ b/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt @@ -0,0 +1,13 @@ +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) +} diff --git a/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt b/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt new file mode 100644 index 0000000..2de0c10 --- /dev/null +++ b/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt @@ -0,0 +1,15 @@ +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.model.StopTime +import kotlin.time.Clock + +expect class StopTimeRepository { + suspend fun getForStop( + id: String, + date: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault()), + ): Flow> +} diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt b/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt similarity index 58% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt rename to core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt index 78ca64b..03ebbda 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt +++ b/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt @@ -4,23 +4,19 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.withContext import kotlinx.datetime.LocalDate -import kotlinx.datetime.TimeZone -import kotlinx.datetime.todayIn -import moe.lava.banksia.core.model.StopTimeDated +import moe.lava.banksia.core.model.StopTime import moe.lava.banksia.core.model.atDate import moe.lava.banksia.core.sqld.StopTimeQueries import moe.lava.banksia.core.sqld.mappers.asModel import moe.lava.banksia.core.util.serialise -import kotlin.time.Clock +import org.koin.core.component.KoinComponent +import org.koin.core.component.get -internal class StopTimeLocalDataSource( - private val queries: StopTimeQueries, -) { - suspend fun getAtStop( - stopId: String, - date: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault()), - ): List { - return withContext(Dispatchers.IO) { +internal class StopTimeLocalDataSource : KoinComponent { + private val queries get() = get() + + suspend fun getAtStop(stopId: String, date: LocalDate): List { + return withContext(context = Dispatchers.IO) { queries .getForStopDated( listOf(date.dayOfWeek).serialise().toLong(), @@ -29,7 +25,7 @@ internal class StopTimeLocalDataSource( ) .executeAsList() .map { it.asModel().atDate(date) } - .sortedBy { it.departureTime } + .sortedBy { it.time.departure } } } } diff --git a/core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt b/core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt new file mode 100644 index 0000000..70ef406 --- /dev/null +++ b/core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt @@ -0,0 +1,9 @@ +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) +} diff --git a/core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt b/core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt new file mode 100644 index 0000000..b4c37a6 --- /dev/null +++ b/core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt @@ -0,0 +1,13 @@ +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)) + } +} diff --git a/core/sqld/build.gradle.kts b/core/sqld/build.gradle.kts index d86247a..472a908 100644 --- a/core/sqld/build.gradle.kts +++ b/core/sqld/build.gradle.kts @@ -47,6 +47,7 @@ sqldelight { databases { register("BanksiaDatabase") { packageName.set("moe.lava.banksia.core.sqld") + schemaOutputDirectory.set(file("src/commonMain/sqldelight/schema")) } } } diff --git a/core/sqld/src/androidMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.android.kt b/core/sqld/src/androidMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.android.kt index b9f2247..c47613c 100644 --- a/core/sqld/src/androidMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.android.kt +++ b/core/sqld/src/androidMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.android.kt @@ -5,10 +5,10 @@ import app.cash.sqldelight.driver.android.AndroidSqliteDriver import org.koin.core.component.KoinComponent import org.koin.core.component.get -actual class DatabaseManager actual constructor() : KoinComponent { +actual class DatabaseManager : KoinComponent { actual val database by lazy { val ctx = get().applicationContext - val driver = AndroidSqliteDriver(BanksiaDatabase.Schema, ctx, "timetable.db") + val driver = AndroidSqliteDriver(BanksiaDatabase.Schema, ctx, "${DBNAME}.db") BanksiaDatabase(driver) } } diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.kt index c6b29f1..983eb58 100644 --- a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.kt +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/DatabaseManager.kt @@ -1,7 +1,7 @@ package moe.lava.banksia.core.sqld -import org.koin.core.component.KoinComponent +internal const val DBNAME = "timetable" -expect class DatabaseManager() : KoinComponent { +expect class DatabaseManager() { val database: BanksiaDatabase } diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/SqldDiModule.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/SqldDiModule.kt index 24ab9bd..deee453 100644 --- a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/SqldDiModule.kt +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/SqldDiModule.kt @@ -11,6 +11,7 @@ val sqldDiModule = module { factory { get().serviceExceptionQueries } factory { get().shapeQueries } factory { get().stopQueries } + factory { get().stoppingPatternQueries } factory { get().stopTimeQueries } factory { get().tripQueries } } diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StopTime.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StopTime.kt index c8b1c44..26d5390 100644 --- a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StopTime.kt +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StopTime.kt @@ -3,23 +3,25 @@ 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( - tripId = tripId, + patternId = patternId, stopId = stopId, - arrivalTime = FutureTime.fromInt(arrivalTime.toInt()), - departureTime = FutureTime.fromInt(departureTime.toInt()), - headsign = null, + time = TimeType.Undated( + arrival = FutureTime.fromInt((departureTime + arrivalDelta).toInt()), + departure = FutureTime.fromInt(departureTime.toInt()), + ), pickupType = pickupType.toInt(), dropOffType = dropOffType.toInt(), ) -fun StopTime.asDb() = DbStopTime( - tripId = tripId, +fun StopTime.Undated.asDb() = DbStopTime( + patternId = patternId, stopId = stopId, - arrivalTime = arrivalTime.asInt().toLong(), - departureTime = departureTime.asInt().toLong(), + arrivalDelta = (time.arrival.asInt() - time.departure.asInt()).toLong(), + departureTime = time.departure.asInt().toLong(), pickupType = pickupType.toLong(), dropOffType = dropOffType.toLong(), ) diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StoppingPattern.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StoppingPattern.kt new file mode 100644 index 0000000..e50aa85 --- /dev/null +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StoppingPattern.kt @@ -0,0 +1,22 @@ +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.sqld.StoppingPattern as DbStoppingPattern + +fun DbStoppingPattern.asModel(stoptimes: List) = StoppingPattern.Undated( + id = id, + routeId = routeId, + shapeId = shapeId, + headsign = headsign, + wheelchairAccessible = wheelchairAccessible == 1L, + stoptimes = stoptimes, +) + +fun StoppingPattern.Undated.asDb() = DbStoppingPattern( + id = id, + routeId = routeId, + shapeId = shapeId, + headsign = headsign, + wheelchairAccessible = if (wheelchairAccessible) 1L else 0L, +) diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Trip.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Trip.kt index 36cdad5..b3443fb 100644 --- a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Trip.kt +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/Trip.kt @@ -1,32 +1,27 @@ 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(service: Service): Trip { +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 = id, - routeId = routeId, + id = gtfsId, + pattern = pattern, service = service, - shapeId = shapeId, - tripHeadsign = tripHeadsign, - directionId = directionId, - blockId = blockId, - wheelchairAccessible = wheelchairAccessible == 1L + directionId = directionId.toInt(), + blockId = blockId.toString(), ) } -fun Trip.asDb() = DbTrip( - id = id, - routeId = routeId, +fun Trip.Undated.asDb() = DbTrip( + gtfsId = id, + patternId = pattern.id, serviceId = service.id, - shapeId = shapeId, - tripHeadsign = tripHeadsign, - directionId = directionId, - blockId = blockId, - wheelchairAccessible = if (wheelchairAccessible) 1L else 0L + directionId = directionId.toLong(), + blockId = blockId?.toLong(), ) diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Stop.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Stop.sq index 3ac06b9..4af5c50 100644 --- a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Stop.sq +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Stop.sq @@ -15,7 +15,7 @@ getAll: SELECT * FROM Stop; getAllParentless: -SELECT * FROM Stop WHERE platformCode IS NULL AND parent IS NULL; +SELECT * FROM Stop WHERE platformCode IS NOT NULL AND parent IS NULL; get: SELECT * FROM Stop WHERE id == ?; @@ -32,8 +32,8 @@ UPDATE Stop SET parent = ? WHERE id IN ?; getByRoute: SELECT Stop.* FROM Stop INNER JOIN StopTime ON StopTime.stopId == Stop.id -INNER JOIN Trip ON Trip.id == StopTime.tripId -WHERE Trip.routeId == :id +INNER JOIN StoppingPattern ON StoppingPattern.id == StopTime.patternId +WHERE StoppingPattern.routeId == :id GROUP BY Stop.id; -- I vibecoded this, sorry @@ -41,8 +41,8 @@ getParentsByRoute: WITH RECURSIVE Tree AS ( SELECT Stop.* FROM Stop INNER JOIN StopTime ON StopTime.stopId == Stop.id - INNER JOIN Trip ON Trip.id == StopTime.tripId - WHERE Trip.routeId == :id + INNER JOIN StoppingPattern ON StoppingPattern.id == StopTime.patternId + WHERE StoppingPattern.routeId == :id GROUP BY Stop.id UNION ALL diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq index f2dc824..45b3f10 100644 --- a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq @@ -1,11 +1,11 @@ CREATE TABLE StopTime ( - tripId TEXT NOT NULL REFERENCES Trip (id), + patternId INTEGER NOT NULL REFERENCES StoppingPattern (id), stopId TEXT NOT NULL REFERENCES Stop (id), - arrivalTime INTEGER NOT NULL, + arrivalDelta INTEGER NOT NULL, departureTime INTEGER NOT NULL, pickupType INTEGER NOT NULL, dropOffType INTEGER NOT NULL, - PRIMARY KEY (tripId, stopId) + PRIMARY KEY (patternId, stopId) ) WITHOUT ROWID; CREATE INDEX idx_StopTime_stopId ON StopTime (stopId); @@ -16,8 +16,9 @@ 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` -INNER JOIN Trip ON Trip.serviceId == Service.id LEFT JOIN ServiceException ON ServiceException.serviceId == Service.id AND ServiceException.date == :date -WHERE StopTime.tripId == Trip.id +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; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StoppingPattern.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StoppingPattern.sq new file mode 100644 index 0000000..cc1c5ab --- /dev/null +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StoppingPattern.sq @@ -0,0 +1,10 @@ +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 ?; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Trip.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Trip.sq index 0fd3c1f..c53b62a 100644 --- a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Trip.sq +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Trip.sq @@ -1,14 +1,12 @@ CREATE TABLE Trip ( - id TEXT PRIMARY KEY NOT NULL, - routeId TEXT NOT NULL REFERENCES Route (id), + gtfsId TEXT PRIMARY KEY NOT NULL, + patternId INTEGER NOT NULL REFERENCES StoppingPattern (id), serviceId TEXT NOT NULL REFERENCES Service (id), - shapeId TEXT NOT NULL REFERENCES Shape (id), - tripHeadsign TEXT NOT NULL, - directionId TEXT NOT NULL, - blockId TEXT, - wheelchairAccessible INTEGER NOT NULL + blockId INTEGER, + directionId INTEGER NOT NULL ); +CREATE INDEX idx_Trip_patternId ON Trip (patternId); CREATE INDEX idx_Trip_serviceId ON Trip (serviceId); insert: diff --git a/core/sqld/src/commonMain/sqldelight/schema/1.db b/core/sqld/src/commonMain/sqldelight/schema/1.db new file mode 100644 index 0000000000000000000000000000000000000000..feaacb3260f779a72f844ab265f00a2d091b9bb1 GIT binary patch literal 81920 zcmWFz^vNtqRY=P(%1ta$FlG>7U}R))P*7lCVBlq7U=U$I01%%A!DV1XV&h`+GU)aG z=H>svAk1dRz;D5SmM@W?lQ)V_f#*1{KKCS^d0cv&>|CtuZ`fzBd$GM?o62T~%cxQD z(GVC7fzc2c4S~TE0-?fe;)bG(#hH02sTG+i74acOnFaC1sYPX($*Ge ztAeMWi)(}erWyr*KLwCF1r4}1%}^n1#ug-&l%y8rL5vl~ZY*34!dSSFW{e=4xUno_ zIo!3uCHVy*nYpR)#U=R#o+&V{5Qf<>8K^>#;V>Zu4Txq<9|1OTZBfQ_6w~7i5{pvv zN2105efR1EN7Qh@VYdSDG;!VHzY*<5LnVi-RF-0ZbDi5@1DOBO!bR4UjI) zCJ8=vacynJ=Jb-pq?}ZcqY5(f(gPsjipJ%K8Xn^4J5aj9W7!;}C?HZ}z=O3cr7wY4q16EX&Us{stnW7Nl8W94QQV4Q&a}9F!b9N0@ z2m&ck(8x^DglH(vNG!msAsD0pNkc|zVoGsldLF735W~tdQd4u1GZHh49Fvn%i;FXp za#G=rhB=f=v$2|oU0hU@u{jbPG@t|t!n`Pf01|=vJiR2X80IJ3UWX?=3>%UCj}f+r zkbp!V%p#a`Q8Yu8AcaO!PJVK>Cp=~#!H|+!l$u>N z$0#9Dl3#!n42eZWnPrJNE~z;si5S5T4V2V^#G;bYqEwI{aHuKBOwKMX2&pWjkT3%k5a3dR12xn@;zS2qL1IaUf|HNG6FhvlG&MQc#BIfKHS0A)mI84l;Op}G`JHPq4YDiR!QaCbq%uLMsbO)SBc zevy+6q6*eg0GX!ArD@N`CLXMd)5B=aj5kF07JH*AGrPF7G-Hz@axg%Zv0(Ew)USlo zHKTpxDEpoOekIg z3lZ%hP?+OzbY5w0Qfd)6+99!xnW?!nHJR|GPOw|z4Z##M0|NsGs2vIF)H4WxSfh9} z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtwau02jz(qy2wSD2(FK5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2cwjnS&|37S_cGP2|Aut*OqaiRF0;3@? z8UmvsFd71*Aut*OqaiRF0;3@S4}sD7e|S)gGDbsSGz3ONU^E0qLtr!nMnhmU1V%$( zGz3ONU^E0qLtxm3!07z{u#MVLkBx@FXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1J zh5$SSM(6+GK{3i04S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=CVH*OY^Z&y( zYDYab8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*O@DLcC|Az;~C}T7PMnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz5li2#n7E58J36_1I_#jE2By2#kinXb6mk zz-S1JhQMeDjE2By2#kinXb8YVV08W;9u%XD(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7 zfzc2c4S~@R7`7oW+W#N6Q9J6f(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R zfQP{7`hR#(j50<;U^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhoOhQR3j|FDhP zQICy=z-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjD`R_1V-oo;XyIV7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fngg0qx1j6Hfl#bHW~t>Aut*OqaiRF0;3@? z8UmvsFd71*Aut*OqaiRF0&p( + val patternId: Long, val stopId: String, - val arrivalTime: FutureTime, - val departureTime: FutureTime, - val headsign: String?, + val time: T, val pickupType: Int, val dropOffType: Int, +) { + typealias Dated = StopTime + typealias Undated = StopTime +} + +@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 StopTime.atDate(date: LocalDate) = StopTime( + patternId = patternId, + stopId = stopId, + time = TimeType.Dated( + arrival = time.arrival.atDate(date), + departure = time.departure.atDate(date), + ), + pickupType = pickupType, + dropOffType = dropOffType, ) diff --git a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StopTimeDated.kt b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StopTimeDated.kt deleted file mode 100644 index 1bd75c6..0000000 --- a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StopTimeDated.kt +++ /dev/null @@ -1,26 +0,0 @@ -package moe.lava.banksia.core.model - -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime -import kotlinx.serialization.Serializable - -@Serializable -data class StopTimeDated( - val tripId: String, - val stopId: String, - val arrivalTime: LocalDateTime, - val departureTime: LocalDateTime, - val headsign: String?, - val pickupType: Int, - val dropOffType: Int, -) - -fun StopTime.atDate(date: LocalDate) = StopTimeDated( - tripId = tripId, - stopId = stopId, - arrivalTime = arrivalTime.atDate(date), - departureTime = departureTime.atDate(date), - headsign = headsign, - pickupType = pickupType, - dropOffType = dropOffType, -) diff --git a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StoppingPattern.kt b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StoppingPattern.kt new file mode 100644 index 0000000..1374cff --- /dev/null +++ b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StoppingPattern.kt @@ -0,0 +1,16 @@ +package moe.lava.banksia.core.model + +import kotlinx.serialization.Serializable + +@Serializable +data class StoppingPattern( + val id: Long, + val routeId: String, + val shapeId: String, + val headsign: String, + val wheelchairAccessible: Boolean, + val stoptimes: List>, +) { + typealias Dated = StoppingPattern + typealias Undated = StoppingPattern +} diff --git a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Trip.kt b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Trip.kt index 6edb538..752d6d2 100644 --- a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Trip.kt +++ b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/Trip.kt @@ -3,13 +3,13 @@ package moe.lava.banksia.core.model import kotlinx.serialization.Serializable @Serializable -data class Trip( +data class Trip( val id: String, - val routeId: String, + val pattern: StoppingPattern, val service: Service, - val shapeId: String, - val tripHeadsign: String, - val directionId: String, + val directionId: Int, val blockId: String?, - val wheelchairAccessible: Boolean, -) +) { + typealias Dated = Trip + typealias Undated = Trip +} diff --git a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt index 1a5ec29..c844499 100644 --- a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt +++ b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt @@ -18,6 +18,7 @@ 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 @@ -25,6 +26,8 @@ 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 @@ -35,6 +38,8 @@ 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 @@ -46,8 +51,7 @@ sealed class GtfsData { data class ServiceExceptionChunk(val exceptions: List) : GtfsData() data class ShapeChunk(val shapes: List) : GtfsData() data class StopChunk(val stops: List) : GtfsData() - data class StopTimeChunk(val stopTimes: List) : GtfsData() - data class TripChunk(val trips: List) : GtfsData() + data class TripChunk(val trips: List) : GtfsData() } class GtfsParser( @@ -129,7 +133,6 @@ class GtfsParser( .filter { it.name == "trips.txt" } .flatMap { fd -> parseTrips(fd, services) - .also { emit(GtfsData.TripChunk(it)) } } .associateBy { it.id } @@ -137,13 +140,53 @@ class GtfsParser( .filter { it.name == "stop_times.txt" } .forEach { fd -> log.info("parsing stop times for ${fd.parent}...") - parseStopTimes(fd, trips) { seq -> - seq.chunked(1000000) - .forEach { emit(GtfsData.StopTimeChunk(it)) } + parseStopTimes(fd) { seq -> + val times = ArrayList>(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): 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, times: ArrayList>) = + 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() .map { with(it) { @@ -180,16 +223,17 @@ class GtfsParser( ) } } - private inline fun parseStopTimes(fd: File, trips: Map, block: (Sequence) -> Unit) = + private inline fun parseStopTimes(fd: File, block: (Sequence>) -> Unit) = fd.parseCsvSequence { seq -> seq .map { with(it) { - StopTime( - tripId = trip_id, + it.trip_id to StopTime( + patternId = stop_sequence, stopId = stop_id, - arrivalTime = GtfsStopTime.parseGtfsTime(arrival_time), - departureTime = GtfsStopTime.parseGtfsTime(departure_time), - headsign = stop_headsign.ifEmpty { trips[trip_id]!!.tripHeadsign }, + time = TimeType.Undated( + arrival = GtfsStopTime.parseGtfsTime(arrival_time), + departure = GtfsStopTime.parseGtfsTime(departure_time), + ), pickupType = pickup_type, dropOffType = drop_off_type, ) @@ -230,15 +274,19 @@ class GtfsParser( private fun parseTrips(fd: File, services: Map) = fd.parseCsv() .map { with(it) { - Trip( + Trip.Undated( id = trip_id, - routeId = route_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}"]!!, - shapeId = shape_id, - tripHeadsign = trip_headsign, - directionId = direction_id, + directionId = direction_id.toInt(), blockId = block_id.ifEmpty { null }, - wheelchairAccessible = wheelchair_accessible == "1", ) } } diff --git a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/structures/GtfsStopTime.kt b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/structures/GtfsStopTime.kt index 33da78f..c0bbaf2 100644 --- a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/structures/GtfsStopTime.kt +++ b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/structures/GtfsStopTime.kt @@ -10,7 +10,7 @@ internal data class GtfsStopTime( val arrival_time: String, val departure_time: String, val stop_id: String, - val stop_sequence: Int, + val stop_sequence: Long, val stop_headsign: String, val pickup_type: Int, val drop_off_type: Int, diff --git a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt index 9ffd03a..17a9002 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt @@ -151,7 +151,7 @@ fun Application.module() { ) .executeAsList() .map { it.asModel().atDate(date) } - .sortedBy { it.departureTime } + .sortedBy { it.time.departure } } call.respond(times) } diff --git a/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt b/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt index 17ab1f5..84fae70 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt @@ -6,7 +6,6 @@ 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.Trip import moe.lava.banksia.core.sqld.DatabaseManager import moe.lava.banksia.core.sqld.mappers.asDb @@ -30,7 +29,6 @@ class GtfsImporter( is GtfsData.ServiceExceptionChunk -> database.addServiceExceptions(chunk.exceptions) is GtfsData.ShapeChunk -> database.addShapes(chunk.shapes) is GtfsData.StopChunk -> database.addStops(chunk.stops) - is GtfsData.StopTimeChunk -> database.addStopTimes(chunk.stopTimes) is GtfsData.TripChunk -> database.addTrips(chunk.trips) } } @@ -101,21 +99,15 @@ class GtfsImporter( log.info("done") } - private fun Database.addStopTimes(stopTimes: List) { - log.info("inserting ${stopTimes.size} stoptimes...") - stopTimeQueries.transaction { - stopTimes.forEach { - stopTimeQueries.insert(it.asDb()) - } - } - log.info("done") - } - - private fun Database.addTrips(trips: List) { + private fun Database.addTrips(trips: List) { log.info("inserting ${trips.size} trips...") - tripQueries.transaction { - trips.forEach { - tripQueries.insert(it.asDb()) + 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") diff --git a/server/src/main/resources/logback.xml b/server/src/main/resources/logback.xml index de5d8bf..6519371 100644 --- a/server/src/main/resources/logback.xml +++ b/server/src/main/resources/logback.xml @@ -14,7 +14,7 @@ %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + diff --git a/settings.gradle.kts b/settings.gradle.kts index 78831ff..28f535e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -39,6 +39,7 @@ include(":core") include(":core:data") include(":core:data:client") include(":core:data:server") +include(":core:data:stoptime") include(":core:sqld") include(":ui") include(":ui:maps") diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt index c4bd768..76fb51e 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt @@ -231,30 +231,36 @@ class MapScreenViewModel( ) } - val departures = stopTimeRepository.getForStop(id) - .filter { !it.headsign.isNullOrBlank() } - .groupBy { it.headsign!! } - .map { (headsign, stopTimes) -> - val now = Clock.System.now() - val times = stopTimes - .map { it.arrivalTime.toInstant(TimeZone.currentSystemDefault()) } - .filter { it >= (now - 1.minutes) } - .joinToString(" | ") { - val diff = (it - now).inWholeMinutes.coerceAtLeast(0) - if (diff >= 65) { - "${((diff + 30.0) / 60.0).toInt()}hr" - } else { - "${diff}mn" - } + stopTimeRepository.getForStop(id) + .onEach { stoptimes -> + val departures = stoptimes +// .filter { !it.headsign.isNullOrBlank() } +// .groupBy { it.headsign!! } + .groupBy { it.stopId } // TODO: Placeholder + .map { (headsign, stopTimes) -> + val now = Clock.System.now() + val times = stopTimes + .map { it.time.arrival.toInstant(TimeZone.currentSystemDefault()) } + .filter { it >= (now - 1.minutes) } + .joinToString(" | ") { + val diff = (it - now).inWholeMinutes.coerceAtLeast(0) + if (diff >= 65) { + "${((diff + 30.0) / 60.0).toInt()}hr" + } else { + "${diff}mn" + } + } + StopInfoPanelState.Departure(headsign, times) } - StopInfoPanelState.Departure(headsign, times) + + iInfoState.update { + if (it !is StopInfoPanelState) + it + else + it.copy(departures = departures) + } } - iInfoState.update { - if (it !is StopInfoPanelState) - it - else - it.copy(departures = departures) - } + .launchIn(viewModelScope) } /*private suspend fun buildPolylines(route: PtvRoute) { From b31067992d0cf5fd9f1a17296e862baec865e2b7 Mon Sep 17 00:00:00 2001 From: Cilly Leang Date: Wed, 27 May 2026 16:35:23 +1000 Subject: [PATCH 5/6] feat(server/gtfsr): compress after every day --- .../moe/lava/banksia/server/gtfsrt/GtfsrtArchiver.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/gtfs_rt/src/main/kotlin/moe/lava/banksia/server/gtfsrt/GtfsrtArchiver.kt b/server/gtfs_rt/src/main/kotlin/moe/lava/banksia/server/gtfsrt/GtfsrtArchiver.kt index 7b9229b..aaee0a9 100644 --- a/server/gtfs_rt/src/main/kotlin/moe/lava/banksia/server/gtfsrt/GtfsrtArchiver.kt +++ b/server/gtfs_rt/src/main/kotlin/moe/lava/banksia/server/gtfsrt/GtfsrtArchiver.kt @@ -41,13 +41,19 @@ internal class GtfsrtArchiver { 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}-${((time.dayOfYear - 1) / 7).toString().padStart(2, '0')}") - val currentParent = File(base, "${time.year}-${((time.dayOfYear - 1) / 7 + 1).toString().padStart(2, '0')}") + 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()) { @@ -90,6 +96,7 @@ internal class GtfsrtArchiver { ).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 { From 8b3016004bdf922a01cd908dbbfd93324e55b784 Mon Sep 17 00:00:00 2001 From: Cilly Leang Date: Tue, 23 Jun 2026 00:07:10 +1000 Subject: [PATCH 6/6] feat: basic departures support also a huge refactor to simplify modules --- core/data/build.gradle.kts | 33 +- core/data/client/build.gradle.kts | 54 --- core/data/server/build.gradle.kts | 20 - .../banksia/core/data/DataDiModule.client.kt} | 6 +- .../repositories/ClientRouteRepository.kt | 10 + .../data/repositories/ClientStopRepository.kt | 0 .../sources/route/RouteLocalDataSource.kt | 1 + .../sources/route/RouteRemoteDataSource.kt | 1 + .../data/sources/stop/StopLocalDataSource.kt | 0 .../data/sources/stop/StopRemoteDataSource.kt | 0 .../lava/banksia/core/data/DataDiModule.kt | 13 + .../core/data/repositories/RouteRepository.kt | 3 +- .../banksia/core/data/DataDiModule.jvm.kt | 7 + .../core/sqld/mappers/StoppingPattern.kt | 5 +- .../moe/lava/banksia/core/sqld/Route.sq | 7 +- .../moe/lava/banksia/core/sqld/StopTime.sq | 21 + .../lava/banksia/core/sqld/StoppingPattern.sq | 3 + .../lava/banksia/core/endpoints/Endpoint.kt | 3 + .../moe/lava/banksia/core/model/StopTime.kt | 10 +- core/{data => }/stoptime/build.gradle.kts | 6 +- .../core/data/StopTimeDataDiModule.client.kt | 0 .../repositories/StopTimeRepository.client.kt | 0 .../stoptime/StopTimeRemoteDataSource.kt | 10 +- .../banksia/core/data/StopTimeDataDiModule.kt | 0 .../banksia/core/data/dto/ExtendedStopTime.kt | 34 ++ .../data/repositories/StopTimeRepository.kt | 4 +- .../stoptime/StopTimeLocalDataSource.kt | 11 +- .../core/endpoints/StopTimeEndpoints.kt | 3 + .../core/data/StopTimeDataDiModule.jvm.kt | 0 .../repositories/StopTimeRepository.jvm.kt | 0 .../banksia/server/routes/StopTimeRoute.kt | 27 ++ gradle/libs.versions.toml | 8 +- server/build.gradle.kts | 2 + .../moe/lava/banksia/server/Application.kt | 28 +- .../lava/banksia/server/di/ServerModules.kt | 4 +- settings.gradle.kts | 4 +- ui/build.gradle.kts | 5 +- ui/shared/build.gradle.kts | 5 + .../drawable/arrow_drop_down.xml | 9 + .../drawable/arrow_drop_up.xml | 9 + .../moe/lava/banksia/ui/di/AppModule.kt | 4 +- .../lava/banksia/ui/layout/info/InfoPanel.kt | 9 +- .../banksia/ui/layout/info/StopInfoPanel.kt | 359 ++++++++++++++++-- .../ui/screens/map/MapScreenViewModel.kt | 100 +++-- 44 files changed, 627 insertions(+), 211 deletions(-) delete mode 100644 core/data/client/build.gradle.kts delete mode 100644 core/data/server/build.gradle.kts rename core/data/{client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt => src/clientMain/kotlin/moe/lava/banksia/core/data/DataDiModule.client.kt} (93%) rename core/data/{client/src/commonMain => src/clientMain}/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt (71%) rename core/data/{client/src/commonMain => src/clientMain}/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt (100%) rename core/data/{client/src/commonMain => src/clientMain}/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt (92%) rename core/data/{client/src/commonMain => src/clientMain}/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt (78%) rename core/data/{client/src/commonMain => src/clientMain}/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt (100%) rename core/data/{client/src/commonMain => src/clientMain}/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt (100%) create mode 100644 core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/DataDiModule.kt create mode 100644 core/data/src/jvmMain/kotlin/moe/lava/banksia/core/data/DataDiModule.jvm.kt create mode 100644 core/src/commonMain/kotlin/moe/lava/banksia/core/endpoints/Endpoint.kt rename core/{data => }/stoptime/build.gradle.kts (90%) rename core/{data => }/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt (100%) rename core/{data => }/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt (100%) rename core/{data => }/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt (66%) rename core/{data => }/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt (100%) create mode 100644 core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/dto/ExtendedStopTime.kt rename core/{data => }/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt (80%) rename core/{data => }/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt (78%) create mode 100644 core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/endpoints/StopTimeEndpoints.kt rename core/{data => }/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt (100%) rename core/{data => }/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt (100%) create mode 100644 core/stoptime/src/jvmMain/kotlin/moe/lava/banksia/server/routes/StopTimeRoute.kt create mode 100644 ui/shared/src/commonMain/composeResources/drawable/arrow_drop_down.xml create mode 100644 ui/shared/src/commonMain/composeResources/drawable/arrow_drop_up.xml diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 8c89aff..ecdba19 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -25,9 +25,40 @@ kotlin { 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.data.stoptime) + 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) } } } diff --git a/core/data/client/build.gradle.kts b/core/data/client/build.gradle.kts deleted file mode 100644 index c6d5e5d..0000000 --- a/core/data/client/build.gradle.kts +++ /dev/null @@ -1,54 +0,0 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.kotlinSerialization) - alias(libs.plugins.androidMultiplatformLibrary) - alias(libs.plugins.ksp) -} - -kotlin { - android { - namespace = "moe.lava.banksia.core.data.client" - compileSdk = libs.versions.android.compileSdk.get().toInt() - - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } - } - - compilerOptions { - freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime") - } - - iosArm64() - iosSimulatorArm64() - - jvm() - - sourceSets { - androidMain.dependencies { - implementation(libs.koin.compose) - implementation(libs.ktor.client.okhttp) - } - commonMain.dependencies { - api(projects.core.data) - - implementation(libs.okio) - implementation(libs.koin.core) - implementation(libs.ktor.client.core) - implementation(libs.ktor.client.contentnegotiation) - implementation(libs.ktor.serialization.kotlinx.json) - implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlinx.datetime) - implementation(libs.kotlinx.serialization.json) - implementation(libs.kotlinx.serialization.protobuf) - - implementation(projects.core) - implementation(projects.core.sqld) - } - iosMain.dependencies { - implementation(libs.ktor.client.darwin) - } - } -} diff --git a/core/data/server/build.gradle.kts b/core/data/server/build.gradle.kts deleted file mode 100644 index d2296d7..0000000 --- a/core/data/server/build.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - alias(libs.plugins.kotlinJvm) - alias(libs.plugins.kotlinSerialization) -} - -kotlin { - compilerOptions { - freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime") - } -} - -dependencies { - implementation(libs.okio) - implementation(libs.koin.core) - implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlinx.datetime) - - api(projects.core.data) - implementation(projects.core) -} diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/DataDiModule.client.kt similarity index 93% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt rename to core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/DataDiModule.client.kt index 0384f88..104c6bc 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/ClientDataDiModule.kt +++ b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/DataDiModule.client.kt @@ -16,17 +16,13 @@ import moe.lava.banksia.core.data.sources.route.RouteLocalDataSource import moe.lava.banksia.core.data.sources.route.RouteRemoteDataSource import moe.lava.banksia.core.data.sources.stop.StopLocalDataSource import moe.lava.banksia.core.data.sources.stop.StopRemoteDataSource -import moe.lava.banksia.core.sqld.sqldDiModule import moe.lava.banksia.core.util.log import moe.lava.banksia.data.ptv.PtvService import org.koin.core.module.dsl.singleOf import org.koin.dsl.bind import org.koin.dsl.module -val clientDataDiModule = module { - includes(sqldDiModule) - includes(stopTimeDataDiModule) - +actual val platformModule = module { // HTTP Clients singleOf(::PtvService) single { diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt similarity index 71% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt rename to core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt index 467399a..f46caac 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt +++ b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/ClientRouteRepository.kt @@ -4,6 +4,7 @@ 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( @@ -22,5 +23,14 @@ internal class ClientRouteRepository internal constructor( } } + private val tripRouteMap = mutableMapOf() + 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 + } + } } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt rename to core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/ClientStopRepository.kt diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt similarity index 92% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt rename to core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt index 8e6af0e..8286b1f 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt +++ b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteLocalDataSource.kt @@ -10,6 +10,7 @@ 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 { diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt similarity index 78% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt rename to core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt index bdcbfc1..15088fb 100644 --- a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt +++ b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/route/RouteRemoteDataSource.kt @@ -7,5 +7,6 @@ import moe.lava.banksia.core.model.Route internal class RouteRemoteDataSource(val client: HttpClient) { suspend fun get(id: String) = client.get("routes/${id}").body() + suspend fun getByPattern(patternId: Long) = client.get("routes/by_pattern/${patternId}").body() suspend fun getAll() = client.get("routes").body>() } diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt rename to core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopLocalDataSource.kt diff --git a/core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt b/core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt similarity index 100% rename from core/data/client/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt rename to core/data/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stop/StopRemoteDataSource.kt diff --git a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/DataDiModule.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/DataDiModule.kt new file mode 100644 index 0000000..eea6a0e --- /dev/null +++ b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/DataDiModule.kt @@ -0,0 +1,13 @@ +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) +} diff --git a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt index fbb663f..ef3d6f1 100644 --- a/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt +++ b/core/data/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/RouteRepository.kt @@ -3,6 +3,7 @@ package moe.lava.banksia.core.data.repositories import moe.lava.banksia.core.model.Route interface RouteRepository { - suspend fun get(id: String): Route + suspend fun get(id: String): Route? + suspend fun getByPattern(patternId: Long): Route? suspend fun getAll(): List } diff --git a/core/data/src/jvmMain/kotlin/moe/lava/banksia/core/data/DataDiModule.jvm.kt b/core/data/src/jvmMain/kotlin/moe/lava/banksia/core/data/DataDiModule.jvm.kt new file mode 100644 index 0000000..78a44d1 --- /dev/null +++ b/core/data/src/jvmMain/kotlin/moe/lava/banksia/core/data/DataDiModule.jvm.kt @@ -0,0 +1,7 @@ +package moe.lava.banksia.core.data + +import org.koin.dsl.module + +internal actual val platformModule = module { + +} diff --git a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StoppingPattern.kt b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StoppingPattern.kt index e50aa85..d1409a2 100644 --- a/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StoppingPattern.kt +++ b/core/sqld/src/commonMain/kotlin/moe/lava/banksia/core/sqld/mappers/StoppingPattern.kt @@ -2,9 +2,10 @@ 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 DbStoppingPattern.asModel(stoptimes: List) = StoppingPattern.Undated( +fun DbStoppingPattern.asModel(stoptimes: List>) = StoppingPattern( id = id, routeId = routeId, shapeId = shapeId, @@ -13,7 +14,7 @@ fun DbStoppingPattern.asModel(stoptimes: List) = StoppingPatte stoptimes = stoptimes, ) -fun StoppingPattern.Undated.asDb() = DbStoppingPattern( +fun StoppingPattern<*>.asDb() = DbStoppingPattern( id = id, routeId = routeId, shapeId = shapeId, diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Route.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Route.sq index f1617f7..e607975 100644 --- a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Route.sq +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/Route.sq @@ -11,5 +11,10 @@ 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 INTO Route VALUES ?; +INSERT OR REPLACE INTO Route VALUES ?; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq index 45b3f10..06bd76b 100644 --- a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StopTime.sq @@ -22,3 +22,24 @@ 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; diff --git a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StoppingPattern.sq b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StoppingPattern.sq index cc1c5ab..9a09e69 100644 --- a/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StoppingPattern.sq +++ b/core/sqld/src/commonMain/sqldelight/moe/lava/banksia/core/sqld/StoppingPattern.sq @@ -8,3 +8,6 @@ CREATE TABLE StoppingPattern ( insert: INSERT OR REPLACE INTO StoppingPattern VALUES ?; + +get: +SELECT * FROM StoppingPattern WHERE id == :id; diff --git a/core/src/commonMain/kotlin/moe/lava/banksia/core/endpoints/Endpoint.kt b/core/src/commonMain/kotlin/moe/lava/banksia/core/endpoints/Endpoint.kt new file mode 100644 index 0000000..7e23b5d --- /dev/null +++ b/core/src/commonMain/kotlin/moe/lava/banksia/core/endpoints/Endpoint.kt @@ -0,0 +1,3 @@ +package moe.lava.banksia.core.endpoints + +object Endpoint diff --git a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StopTime.kt b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StopTime.kt index da92819..edd7c51 100644 --- a/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StopTime.kt +++ b/core/src/commonMain/kotlin/moe/lava/banksia/core/model/StopTime.kt @@ -31,13 +31,15 @@ sealed class TimeType { ) : TimeType() } +fun TimeType.Undated.atDate(date: LocalDate) = TimeType.Dated( + arrival = arrival.atDate(date), + departure = departure.atDate(date), +) + fun StopTime.atDate(date: LocalDate) = StopTime( patternId = patternId, stopId = stopId, - time = TimeType.Dated( - arrival = time.arrival.atDate(date), - departure = time.departure.atDate(date), - ), + time = time.atDate(date), pickupType = pickupType, dropOffType = dropOffType, ) diff --git a/core/data/stoptime/build.gradle.kts b/core/stoptime/build.gradle.kts similarity index 90% rename from core/data/stoptime/build.gradle.kts rename to core/stoptime/build.gradle.kts index 086e749..44cf072 100644 --- a/core/data/stoptime/build.gradle.kts +++ b/core/stoptime/build.gradle.kts @@ -9,7 +9,7 @@ plugins { kotlin { android { - namespace = "moe.lava.banksia.core.data.stoptime" + namespace = "moe.lava.banksia.core.stoptime" compileSdk = libs.versions.android.compileSdk.get().toInt() compilerOptions { @@ -56,5 +56,9 @@ kotlin { iosMain.dependencies { implementation(libs.ktor.client.darwin) } + jvmMain.dependencies { + implementation(libs.koin.ktor) + implementation(libs.ktor.server.core) + } } } diff --git a/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt b/core/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt similarity index 100% rename from core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt rename to core/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.client.kt diff --git a/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt b/core/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt similarity index 100% rename from core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt rename to core/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.client.kt diff --git a/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt b/core/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt similarity index 66% rename from core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt rename to core/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt index 1d338ce..0c38f64 100644 --- a/core/data/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt +++ b/core/stoptime/src/clientMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeRemoteDataSource.kt @@ -7,7 +7,9 @@ import io.ktor.client.request.parameter import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.todayIn -import moe.lava.banksia.core.model.StopTime +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( @@ -16,9 +18,9 @@ internal class StopTimeRemoteDataSource( suspend fun getAtStop( stopId: String, date: LocalDate? = Clock.System.todayIn(TimeZone.currentSystemDefault()), - ): List { - return client.get("stoptimes/by_stop/${stopId}") { + ): List { + return client.get(Endpoint.stopTimeByStop(stopId)) { parameter("date", date) - }.body>() + }.body>() } } diff --git a/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt similarity index 100% rename from core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt rename to core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.kt diff --git a/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/dto/ExtendedStopTime.kt b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/dto/ExtendedStopTime.kt new file mode 100644 index 0000000..38de29d --- /dev/null +++ b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/dto/ExtendedStopTime.kt @@ -0,0 +1,34 @@ +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, +) diff --git a/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt similarity index 80% rename from core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt rename to core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt index 2de0c10..6a81c09 100644 --- a/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt +++ b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.kt @@ -4,12 +4,12 @@ import kotlinx.coroutines.flow.Flow import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.todayIn -import moe.lava.banksia.core.model.StopTime +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> + ): Flow> } diff --git a/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt similarity index 78% rename from core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt rename to core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt index 03ebbda..f22dc09 100644 --- a/core/data/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt +++ b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/data/sources/stoptime/StopTimeLocalDataSource.kt @@ -4,10 +4,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.withContext import kotlinx.datetime.LocalDate -import moe.lava.banksia.core.model.StopTime -import moe.lava.banksia.core.model.atDate +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.sqld.mappers.asModel import moe.lava.banksia.core.util.serialise import org.koin.core.component.KoinComponent import org.koin.core.component.get @@ -15,16 +14,16 @@ import org.koin.core.component.get internal class StopTimeLocalDataSource : KoinComponent { private val queries get() = get() - suspend fun getAtStop(stopId: String, date: LocalDate): List { + suspend fun getAtStop(stopId: String, date: LocalDate): List { return withContext(context = Dispatchers.IO) { queries - .getForStopDated( + .getExtendedForStop( listOf(date.dayOfWeek).serialise().toLong(), date.toEpochDays(), stopId, ) .executeAsList() - .map { it.asModel().atDate(date) } + .map { it.asModel(date) } .sortedBy { it.time.departure } } } diff --git a/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/endpoints/StopTimeEndpoints.kt b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/endpoints/StopTimeEndpoints.kt new file mode 100644 index 0000000..f689b2d --- /dev/null +++ b/core/stoptime/src/commonMain/kotlin/moe/lava/banksia/core/endpoints/StopTimeEndpoints.kt @@ -0,0 +1,3 @@ +package moe.lava.banksia.core.endpoints + +fun Endpoint.stopTimeByStop(stopId: String) = "stoptimes/by_stop/${stopId}" diff --git a/core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt b/core/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt similarity index 100% rename from core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt rename to core/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/StopTimeDataDiModule.jvm.kt diff --git a/core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt b/core/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt similarity index 100% rename from core/data/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt rename to core/stoptime/src/jvmMain/kotlin/moe/lava/banksia/core/data/repositories/StopTimeRepository.jvm.kt diff --git a/core/stoptime/src/jvmMain/kotlin/moe/lava/banksia/server/routes/StopTimeRoute.kt b/core/stoptime/src/jvmMain/kotlin/moe/lava/banksia/server/routes/StopTimeRoute.kt new file mode 100644 index 0000000..5791855 --- /dev/null +++ b/core/stoptime/src/jvmMain/kotlin/moe/lava/banksia/server/routes/StopTimeRoute.kt @@ -0,0 +1,27 @@ +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() + + 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) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 02ca8cc..483c5d5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,11 +1,11 @@ [versions] agp = "9.1.0" -android-compileSdk = "36" +android-compileSdk = "37" android-minSdk = "24" -android-targetSdk = "36" +android-targetSdk = "37" androidx-activity= "1.13.0" androidx-lifecycle = "2.10.0" -compose-multiplatform = "1.11.0-alpha04" +compose-multiplatform = "1.12.0-alpha02" composeunstyled = "1.49.6" coroutines = "1.10.2" geo = "0.8.0" @@ -19,7 +19,7 @@ ktor = "3.4.1" logback = "1.5.32" maplibre = "0.12.1" material = "1.7.3" -material3 = "1.11.0-alpha04" +material3 = "1.11.0-alpha07" okio = "3.17.0" playServicesLocation = "21.3.0" secretsGradlePlugin = "2.0.1" diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 93da532..9d2cb78 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -20,7 +20,9 @@ kotlin { dependencies { implementation(projects.core) + implementation(projects.core.data) implementation(projects.core.sqld) + implementation(projects.core.stoptime) implementation(projects.server.gtfs) implementation(projects.server.gtfsRt) diff --git a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt index 17a9002..dedffe5 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/Application.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/Application.kt @@ -15,22 +15,16 @@ import io.ktor.server.routing.routing import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlinx.datetime.LocalDate -import kotlinx.datetime.TimeZone -import kotlinx.datetime.todayIn import moe.lava.banksia.core.Constants -import moe.lava.banksia.core.model.atDate import moe.lava.banksia.core.sqld.RouteQueries import moe.lava.banksia.core.sqld.StopQueries -import moe.lava.banksia.core.sqld.StopTimeQueries import moe.lava.banksia.core.sqld.mappers.asModel -import moe.lava.banksia.core.util.serialise import moe.lava.banksia.server.di.ServerModules import moe.lava.banksia.server.gtfsrt.GtfsrtService +import moe.lava.banksia.server.routes.stopTimeRoutes import org.koin.dsl.module import org.koin.ktor.ext.get import org.koin.ktor.plugin.Koin -import kotlin.time.Clock fun main() { if (System.getenv("BANKSIA_PRODUCTION") == "1") Constants.devMode = false @@ -53,6 +47,8 @@ fun Application.module() { launch { get().start(this, !Constants.devMode) } routing { + stopTimeRoutes() + if (Constants.devMode) { get("/fixup") { call.respondText("received") @@ -137,23 +133,5 @@ fun Application.module() { } call.respond(stops.map { it.asModel() }) } - 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) { - get() - .getForStopDated( - listOf(date.dayOfWeek).serialise().toLong(), - date.toEpochDays(), - stopId, - ) - .executeAsList() - .map { it.asModel().atDate(date) } - .sortedBy { it.time.departure } - } - call.respond(times) - } } } diff --git a/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt b/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt index 881becf..b2593b3 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/di/ServerModules.kt @@ -1,7 +1,7 @@ package moe.lava.banksia.server.di import io.ktor.client.HttpClient -import moe.lava.banksia.core.sqld.sqldDiModule +import moe.lava.banksia.core.data.dataDiModule import moe.lava.banksia.server.GtfsDataFixer import moe.lava.banksia.server.GtfsImporter import moe.lava.banksia.server.gtfs.GtfsParser @@ -11,7 +11,7 @@ import org.koin.core.module.dsl.singleOf import org.koin.dsl.module val ServerModules = module { - includes(sqldDiModule) + includes(dataDiModule) single { HttpClient() } singleOf(::GtfsParser) diff --git a/settings.gradle.kts b/settings.gradle.kts index 28f535e..bdb499e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,9 +37,7 @@ include(":server:gtfs") include(":server:gtfs_rt") include(":core") include(":core:data") -include(":core:data:client") -include(":core:data:server") -include(":core:data:stoptime") +include(":core:stoptime") include(":core:sqld") include(":ui") include(":ui:maps") diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index 9c5c7bd..b599bc6 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -41,7 +41,9 @@ kotlin { sourceSets { androidMain.dependencies { + implementation(libs.compose.ui.tooling.preview) implementation(libs.play.services.location) + implementation(projects.ui.shared) } commonMain.dependencies { implementation(libs.compose.components.resources) @@ -68,7 +70,8 @@ kotlin { implementation(libs.ui.backhandler) implementation(projects.core) - implementation(projects.core.data.client) + implementation(projects.core.data) + implementation(projects.core.stoptime) implementation(projects.ui.maps) implementation(projects.ui.shared) } diff --git a/ui/shared/build.gradle.kts b/ui/shared/build.gradle.kts index e379840..2a78572 100644 --- a/ui/shared/build.gradle.kts +++ b/ui/shared/build.gradle.kts @@ -16,6 +16,10 @@ kotlin { compilerOptions { jvmTarget.set(JvmTarget.JVM_11) } + + androidResources { + enable = true + } } compilerOptions { @@ -47,4 +51,5 @@ dependencies { compose.resources { publicResClass = true packageOfResClass = "moe.lava.banksia.resources" + generateResClass = always } diff --git a/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_down.xml b/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_down.xml new file mode 100644 index 0000000..ac49572 --- /dev/null +++ b/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_up.xml b/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_up.xml new file mode 100644 index 0000000..322fa56 --- /dev/null +++ b/ui/shared/src/commonMain/composeResources/drawable/arrow_drop_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/di/AppModule.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/di/AppModule.kt index a2b4d7e..cff36fb 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/di/AppModule.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/di/AppModule.kt @@ -1,12 +1,12 @@ package moe.lava.banksia.ui.di -import moe.lava.banksia.core.data.clientDataDiModule +import moe.lava.banksia.core.data.dataDiModule import moe.lava.banksia.ui.screens.map.MapScreenViewModel import org.koin.core.module.dsl.viewModelOf import org.koin.dsl.module val AppModule = module { - includes(clientDataDiModule) + includes(dataDiModule) // ViewModel viewModelOf(::MapScreenViewModel) diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/InfoPanel.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/InfoPanel.kt index 55eac69..9fb37d7 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/InfoPanel.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/InfoPanel.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeContent import androidx.compose.foundation.layout.size @@ -27,7 +28,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.coerceAtMost import androidx.compose.ui.unit.dp import kotlinx.coroutines.delay import kotlin.time.Duration.Companion.milliseconds @@ -45,6 +45,7 @@ sealed class InfoPanelState { @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun InfoPanel( + modifier: Modifier = Modifier, state: InfoPanelState, onEvent: (InfoPanelEvent) -> Unit, onPeekHeightChange: (Dp) -> Unit, @@ -65,11 +66,13 @@ fun InfoPanel( } Column( - Modifier + modifier = modifier .fillMaxWidth() .padding(horizontal = 24.dp) + .heightIn(min = 350.dp) .onSizeChanged { - onPeekHeightChange(with(localDensity) { it.height.toDp().coerceAtMost(250.dp) }) +// onPeekHeightChange(with(localDensity) { it.height.toDp().coerceAtMost(250.dp) }) + onPeekHeightChange(350.dp) } ) { Box { diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/StopInfoPanel.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/StopInfoPanel.kt index dbe3b29..369721c 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/StopInfoPanel.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/layout/info/StopInfoPanel.kt @@ -1,75 +1,358 @@ package moe.lava.banksia.ui.layout.info +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SegmentedListItem +import androidx.compose.material3.ShapeDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import moe.lava.banksia.resources.Res +import moe.lava.banksia.resources.arrow_drop_down +import moe.lava.banksia.resources.arrow_drop_up +import moe.lava.banksia.ui.extensions.BUS_ORANGE +import moe.lava.banksia.ui.extensions.TRAIN_BLUE +import moe.lava.banksia.ui.platform.BanksiaTheme +import org.jetbrains.compose.resources.painterResource +import kotlin.time.Clock +import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Instant -sealed class StopInfoPanelEvent : InfoPanelEvent() +sealed class StopInfoPanelEvent : InfoPanelEvent() { + data object ToggleGrouping : StopInfoPanelEvent() +} data class StopInfoPanelState( val id: String, val name: String, val subname: String? = null, - val departures: List? = null, + val departures: List? = null, ) : InfoPanelState() { override val loading: Boolean - get() = departures == null + get() = departures.isNullOrEmpty() - data class Departure(val directionName: String, val formattedTimes: String) + data class DeparturePlatforms( + val platform: String, + val departures: List, + ) + + data class DepartureInfo( + val routeName: String, + val routeColour: Color?, + val headsign: String, + val description: String?, + val time: Instant, + ) } @Composable -internal fun StopInfoPanel( - state: StopInfoPanelState, - onEvent: (StopInfoPanelEvent) -> Unit, +private fun listColors() = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + selectedContainerColor = MaterialTheme.colorScheme.primary, + selectedContentColor = MaterialTheme.colorScheme.onPrimary, +) + +@Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +private fun MonoPlatform( + state: StopInfoPanelState.DeparturePlatforms ) { - Column(Modifier.fillMaxWidth()) { - Text( - state.name, - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold, - textAlign = TextAlign.Start - ) - state.subname?.let { - Text( - "/ $it", - modifier = Modifier.padding(start = 5.dp), - style = MaterialTheme.typography.titleSmall, - color = Color.Gray, - fontWeight = FontWeight.SemiBold, - textAlign = TextAlign.Start - ) - } - state.departures?.let { - Spacer(Modifier.height(5.dp)) - it.forEach { (name, formatted) -> - Row(verticalAlignment = Alignment.CenterVertically) { + val departures = state.departures + val lazyState = LazyListState(firstVisibleItemIndex = + departures.indexOfFirst { + it.time > Clock.System.now() + }.coerceAtLeast(0) + ) + LazyColumn( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + state = lazyState, + ) { + itemsIndexed(departures) { idx, dep -> + SegmentedListItem( + onClick = {}, + colors = listColors(), + shapes = ListItemDefaults.segmentedShapes( + idx, + departures.size, + ), + supportingContent = { + dep.description?.let { Text(dep.description) } + }, + trailingContent = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy((-4).dp) + ) { + Text( + text = (dep.time - Clock.System.now()).inWholeMinutes.toString(), + style = MaterialTheme.typography.headlineSmallEmphasized, + ) + Text( + text = "mn", + style = MaterialTheme.typography.labelSmallEmphasized, + ) + } + }, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + Box( + Modifier + .clip(ShapeDefaults.ExtraSmall) + .background(dep.routeColour ?: MaterialTheme.colorScheme.surface) + .padding(vertical = 2.dp, horizontal = 4.dp) + ) { + Text( + text = dep.routeName, + style = MaterialTheme.typography.labelSmallEmphasized, + color = MaterialTheme.colorScheme.surface, + ) + } Text( - name, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - Text( - formatted, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 5.dp) + text = dep.headsign, + style = MaterialTheme.typography.labelLargeEmphasized, ) } } } } } + +@Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +private fun ManyPlatforms( + state: List, +) { + val expandedList = remember { mutableStateListOf(*Array(state.size) { true }) } + LazyColumn( + modifier = Modifier.fillMaxSize(), + ) { + state.forEachIndexed { idx, depInfo -> + val (platform, departures) = depInfo + val expanded = expandedList[idx] + stickyHeader(key = "header_${depInfo.hashCode()}") { + val base = ListItemDefaults.segmentedShapes(0, 2) + val large = MaterialTheme.shapes.large + + Box( + Modifier + .animateItem() + .background(MaterialTheme.colorScheme.surfaceContainerLow) + .padding(bottom = ListItemDefaults.SegmentedGap) + ) { + SegmentedListItem( + onClick = { expandedList[idx] = !expandedList[idx] }, + colors = listColors(), + shapes = if (expanded) base else base.copy(shape = large), + trailingContent = { + Icon( + painterResource(if (expanded) Res.drawable.arrow_drop_up else Res.drawable.arrow_drop_down), + contentDescription = null, + modifier = Modifier + .background( + if (expanded) MaterialTheme.colorScheme.surface else Color.Transparent, + shape = RoundedCornerShape(100) + ) + .padding(6.dp), + tint = MaterialTheme.colorScheme.onSurface, + ) + }, + ) { + Text( + text = platform, + style = MaterialTheme.typography.labelLarge, + ) + } + } + } + + if (expanded) { + item(key = "items_${depInfo.hashCode()}") { + Column( + modifier = Modifier.animateItem(), + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { + departures.filter { it.time > Clock.System.now() }.take(5) + .forEachIndexed { idx, dep -> + SegmentedListItem( + onClick = {}, + colors = listColors(), + shapes = ListItemDefaults.segmentedShapes( + idx + 1, + (departures.size + 1).coerceAtMost(6), + ), + supportingContent = { + dep.description?.let { Text(dep.description) } + }, + trailingContent = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy((-4).dp) + ) { + Text( + text = (dep.time - Clock.System.now()).inWholeMinutes.toString(), + style = MaterialTheme.typography.headlineSmallEmphasized, + ) + Text( + text = "mn", + style = MaterialTheme.typography.labelSmallEmphasized, + ) + } + }, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + Box( + Modifier + .clip(ShapeDefaults.ExtraSmall) + .background( + dep.routeColour + ?: MaterialTheme.colorScheme.surface + ) + .padding(vertical = 2.dp, horizontal = 4.dp) + ) { + Text( + text = dep.routeName, + style = MaterialTheme.typography.labelSmallEmphasized, + color = MaterialTheme.colorScheme.surface, + ) + } + Text( + text = dep.headsign, + style = MaterialTheme.typography.labelLargeEmphasized, + ) + } + } + } + } + } + } + item(key = "spacer_${depInfo.hashCode()}") { + Spacer( + modifier = Modifier.animateItem().height(10.dp) + ) + } + } + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +internal fun StopInfoPanel( + state: StopInfoPanelState, + onEvent: (StopInfoPanelEvent) -> Unit, +) { + val spec = fadeIn(tween(300, 300)) togetherWith fadeOut(tween(300)) + + AnimatedContent( + targetState = state, + contentKey = { it.id }, + transitionSpec = { spec }, + ) { state -> + Column(Modifier.fillMaxWidth().fillMaxHeight()) { + Row { + Column { + Text( + state.name, + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.SemiBold, + textAlign = TextAlign.Start + ) + state.subname?.let { + Text( + "/ $it", + modifier = Modifier.padding(start = 5.dp), + style = MaterialTheme.typography.titleSmall, + color = Color.Gray, + fontWeight = FontWeight.SemiBold, + textAlign = TextAlign.Start + ) + } + } + IconButton( + onClick = { onEvent(StopInfoPanelEvent.ToggleGrouping) }, + ) { Icon(Icons.Default.Edit, null) } + } + Spacer(Modifier.height(10.dp)) + AnimatedContent( + targetState = state.departures, + transitionSpec = { spec }, + ) { departures -> + departures?.let { departurePlatforms -> + if (departurePlatforms.size > 1) { + ManyPlatforms(departurePlatforms) + } else if (departurePlatforms.size == 1) { + MonoPlatform(departurePlatforms[0]) + } + } + } + } + } +} + +@Preview +@Composable +internal fun StopInfoPanelPreview() { + fun dateIn(dur: Duration) = (Clock.System.now() + dur) + + InfoPanel( + modifier = Modifier.background(BanksiaTheme.colors.background), + state = StopInfoPanelState( + id = "id", + name = "name", + subname = "sub", + departures = listOf( + StopInfoPanelState.DeparturePlatforms("Platform 1", listOf( + StopInfoPanelState.DepartureInfo("Sunbury", Color(TRAIN_BLUE), "Sunbury", "··· Malvern -> Anzac ··· Sunbury", dateIn(2.minutes)), + StopInfoPanelState.DepartureInfo("Sunbury", Color(TRAIN_BLUE), "West Footscray", "Express via Metro Tunnel", dateIn(8.minutes)), + )), + StopInfoPanelState.DeparturePlatforms("Platform 2", listOf( + StopInfoPanelState.DepartureInfo("237", Color(BUS_ORANGE), "Westall", null, dateIn(7.minutes)), + StopInfoPanelState.DepartureInfo("442", Color(BUS_ORANGE), "Dandenong", null, dateIn(8.minutes)), + )), + ), + ), + onEvent = {}, + onPeekHeightChange = {}, + ) +} diff --git a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt index 76fb51e..de06381 100644 --- a/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt +++ b/ui/src/commonMain/kotlin/moe/lava/banksia/ui/screens/map/MapScreenViewModel.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant +import moe.lava.banksia.core.data.dto.ExtendedStopTime import moe.lava.banksia.core.data.repositories.RouteRepository import moe.lava.banksia.core.data.repositories.StopRepository import moe.lava.banksia.core.data.repositories.StopTimeRepository @@ -26,9 +27,11 @@ import moe.lava.banksia.core.util.LoopFlow.Companion.waitUntilSubscribed import moe.lava.banksia.core.util.Point import moe.lava.banksia.core.util.log import moe.lava.banksia.data.ptv.PtvService +import moe.lava.banksia.ui.extensions.getUIProperties import moe.lava.banksia.ui.layout.info.InfoPanelEvent import moe.lava.banksia.ui.layout.info.InfoPanelState import moe.lava.banksia.ui.layout.info.RouteInfoPanelState +import moe.lava.banksia.ui.layout.info.StopInfoPanelEvent import moe.lava.banksia.ui.layout.info.StopInfoPanelState import moe.lava.banksia.ui.layout.info.TripInfoPanelState import moe.lava.banksia.ui.map.util.CameraPosition @@ -36,8 +39,6 @@ import moe.lava.banksia.ui.map.util.CameraPositionBounds import moe.lava.banksia.ui.map.util.Marker import moe.lava.banksia.ui.state.MapState import moe.lava.banksia.ui.state.SearchState -import kotlin.time.Clock -import kotlin.time.Duration.Companion.minutes sealed class MapScreenEvent { data object DismissState : MapScreenEvent() @@ -53,6 +54,9 @@ private data class InternalState( val route: String? = null, val stop: String? = null, val run: String? = null, + + val lastStopDepartures: List? = null, + val stopsGrouped: Boolean = true, ) class MapScreenViewModel( @@ -69,6 +73,10 @@ class MapScreenViewModel( viewModelScope.launch { switchRoute(value.route) } if (value.stop != last.stop) viewModelScope.launch { switchStop(value.stop) } + if (value.lastStopDepartures != last.lastStopDepartures) + viewModelScope.launch { buildDepartures() } + if (value.stopsGrouped != last.stopsGrouped) + viewModelScope.launch { buildDepartures() } if (value.run != last.run) switchRun(value.run) } @@ -105,7 +113,9 @@ class MapScreenViewModel( fun handleEvent(event: InfoPanelEvent) { viewModelScope.launch { -// when (event) { } + when (event) { + StopInfoPanelEvent.ToggleGrouping -> state = state.copy(stopsGrouped = !state.stopsGrouped) + } } } @@ -165,7 +175,7 @@ class MapScreenViewModel( } val route = routeRepository.get(routeId) -// val gtfsRoute = ptvService.route(routeId) + ?: return iInfoState.update { RouteInfoPanelState( name = route.name, @@ -215,11 +225,11 @@ class MapScreenViewModel( private suspend fun switchStop(id: String?) { if (id == null) { iInfoState.update { InfoPanelState.None } + state = state.copy(lastStopDepartures = null) return } val stop = stopRepository.get(id) -// val stop = ptvService.stop(routeType, stopId) val split = stop.name.split("/") val name = split[0] val subname = split.getOrNull(1) @@ -232,37 +242,63 @@ class MapScreenViewModel( } stopTimeRepository.getForStop(id) - .onEach { stoptimes -> - val departures = stoptimes -// .filter { !it.headsign.isNullOrBlank() } -// .groupBy { it.headsign!! } - .groupBy { it.stopId } // TODO: Placeholder - .map { (headsign, stopTimes) -> - val now = Clock.System.now() - val times = stopTimes - .map { it.time.arrival.toInstant(TimeZone.currentSystemDefault()) } - .filter { it >= (now - 1.minutes) } - .joinToString(" | ") { - val diff = (it - now).inWholeMinutes.coerceAtLeast(0) - if (diff >= 65) { - "${((diff + 30.0) / 60.0).toInt()}hr" - } else { - "${diff}mn" - } - } - StopInfoPanelState.Departure(headsign, times) - } - - iInfoState.update { - if (it !is StopInfoPanelState) - it - else - it.copy(departures = departures) - } + .onEach { departures -> + state = state.copy( + lastStopDepartures = departures + ) } .launchIn(viewModelScope) } + private fun friendlyPlatform(platform: String) = + platform.takeUnless { it.firstOrNull()?.isDigit() == true } + ?: "Platform $platform" + private fun buildDepartures() { + val rawDepartures = state.lastStopDepartures ?: return + val departures = if (state.stopsGrouped) { + rawDepartures + .groupBy { it.stopPlatformCode } + .mapKeys { (platform) -> platform?.let { friendlyPlatform(it) } } + .entries + .sortedBy { (platform) -> platform } + .map { (platform, deps) -> + StopInfoPanelState.DeparturePlatforms( + platform = platform ?: "", + departures = deps.map { + StopInfoPanelState.DepartureInfo( + routeName = it.routeNumber ?: it.routeName, + routeColour = it.routeType.getUIProperties().colour, + headsign = it.headsign ?: it.routeName, + description = null, + time = it.time.departure.toInstant(TimeZone.currentSystemDefault()), + ) + } + ) + } + } else if (rawDepartures.isEmpty()) { + listOf() + } else { + listOf(StopInfoPanelState.DeparturePlatforms(platform = "", departures = rawDepartures.map { dep -> + StopInfoPanelState.DepartureInfo( + routeName = dep.routeNumber ?: dep.routeName, + routeColour = dep.routeType.getUIProperties().colour, + headsign = dep.headsign ?: dep.routeName, + description = dep.stopPlatformCode?.let { friendlyPlatform(it) }, + time = dep.time.departure.toInstant(TimeZone.currentSystemDefault()), + ) + })) + } + + departures.let { departures -> + iInfoState.update { + if (it !is StopInfoPanelState) + it + else + it.copy(departures = departures) + } + } + } + /*private suspend fun buildPolylines(route: PtvRoute) { val routeWithGeo = if (route.geopath.isEmpty()) ptvService.route(route.routeId, true)