refactor: shared -> core

This commit is contained in:
Cilly Leang 2026-04-02 01:57:08 +11:00
parent 104a77b27e
commit c912723c78
Signed by: cilly
GPG key ID: 6500251E087653C9
112 changed files with 133 additions and 140 deletions

63
core/build.gradle.kts Normal file
View file

@ -0,0 +1,63 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
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 {
android {
namespace = "moe.lava.banksia.core"
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 {
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(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)
}

View file

@ -0,0 +1,52 @@
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"
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 {
implementation(libs.okio)
implementation(libs.koin.core)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.contentnegotiation)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.serialization.protobuf)
implementation(projects.core)
implementation(projects.core.room)
}
iosMain.dependencies {
implementation(libs.ktor.client.darwin)
}
}
}

View file

@ -0,0 +1,62 @@
package moe.lava.banksia.core.data
import io.ktor.client.HttpClient
import io.ktor.client.plugins.HttpSend
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.plugin
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
import moe.lava.banksia.core.Constants
import moe.lava.banksia.core.data.repositories.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.room.roomDiModule
import moe.lava.banksia.core.util.log
import moe.lava.banksia.data.ptv.PtvService
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
val dataDiModule = module {
includes(roomDiModule)
// HTTP Clients
singleOf(::PtvService)
single {
HttpClient {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
defaultRequest {
url(Constants.serverUrl)
}
}.also { client ->
client.plugin(HttpSend).intercept { req ->
val fullPath = req.url.build().encodedPathAndQuery
log("ktor.client", fullPath)
execute(req)
}
}
}
// Data sources
singleOf(::RouteLocalDataSource)
singleOf(::RouteRemoteDataSource)
singleOf(::StopLocalDataSource)
singleOf(::StopRemoteDataSource)
singleOf(::StopTimeLocalDataSource)
singleOf(::StopTimeRemoteDataSource)
// Repositories
singleOf(::RouteRepository)
singleOf(::StopRepository)
singleOf(::StopTimeRepository)
}

View file

@ -0,0 +1,25 @@
package moe.lava.banksia.core.data.repositories
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import moe.lava.banksia.core.data.sources.route.RouteLocalDataSource
import moe.lava.banksia.core.data.sources.route.RouteRemoteDataSource
class RouteRepository internal constructor(
private val local: RouteLocalDataSource,
private val remote: RouteRemoteDataSource,
) {
private val mutex = Mutex()
suspend fun getAll() = mutex.withLock {
local
.getAll()
.map { it.asModel() }
.ifEmpty {
remote
.getAll()
.also { local.save(*it.toTypedArray()) }
}
}
suspend fun get(id: String) = mutex.withLock { local.get(id)?.asModel() ?: remote.get(id) }
}

View file

@ -0,0 +1,22 @@
package moe.lava.banksia.core.data.repositories
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import moe.lava.banksia.core.data.sources.stop.StopLocalDataSource
import moe.lava.banksia.core.data.sources.stop.StopRemoteDataSource
class StopRepository internal constructor(
private val local: StopLocalDataSource,
private val remote: StopRemoteDataSource,
) {
private val mutex = Mutex()
suspend fun get(id: String) = mutex.withLock { local.get(id)?.asModel() ?: remote.get(id) }
suspend fun getByRoute(id: String) = mutex.withLock {
local
.getByRoute(id)
.map { it.asModel() }
.ifEmpty { null }
?: remote.getByRoute(id)
}
}

View file

@ -0,0 +1,16 @@
package moe.lava.banksia.core.data.repositories
import moe.lava.banksia.core.data.sources.stoptime.StopTimeLocalDataSource
import moe.lava.banksia.core.data.sources.stoptime.StopTimeRemoteDataSource
import moe.lava.banksia.core.model.StopTimeDated
class StopTimeRepository internal constructor(
private val local: StopTimeLocalDataSource,
private val remote: StopTimeRemoteDataSource,
) {
suspend fun getForStop(id: String): List<StopTimeDated> {
return local
.getAtStop(id)
.ifEmpty { remote.getAtStop(id) }
}
}

View file

@ -0,0 +1,11 @@
package moe.lava.banksia.core.data.sources.route
import moe.lava.banksia.core.model.Route
import moe.lava.banksia.core.room.dao.RouteDao
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 save(vararg routes: Route) = dao.insertOrReplaceAll(*routes.map { it.asEntity() }.toTypedArray())
}

View file

@ -0,0 +1,11 @@
package moe.lava.banksia.core.data.sources.route
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import moe.lava.banksia.core.model.Route
internal class RouteRemoteDataSource(val client: HttpClient) {
suspend fun get(id: String) = client.get("routes/${id}").body<Route>()
suspend fun getAll() = client.get("routes").body<List<Route>>()
}

View file

@ -0,0 +1,12 @@
package moe.lava.banksia.core.data.sources.stop
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
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())
}

View file

@ -0,0 +1,11 @@
package moe.lava.banksia.core.data.sources.stop
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import moe.lava.banksia.core.model.Stop
internal class StopRemoteDataSource(val client: HttpClient) {
suspend fun get(id: String) = client.get("stops/${id}").body<Stop>()
suspend fun getByRoute(id: String) = client.get("route_stops/${id}").body<List<Stop>>()
}

View file

@ -0,0 +1,28 @@
package moe.lava.banksia.core.data.sources.stoptime
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.util.serialise
import kotlin.time.Clock
internal class StopTimeLocalDataSource(
private val stopTimeDao: StopTimeDao,
) {
suspend fun getAtStop(
stopId: String,
date: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault()),
): List<StopTimeDated> {
return stopTimeDao
.getForStopDated(
stopId,
listOf(date.dayOfWeek).serialise(),
date.toEpochDays().toInt(),
)
.map { it.asModel().atDate(date) }
.sortedBy { it.departureTime }
}
}

View file

@ -0,0 +1,36 @@
package moe.lava.banksia.core.data.sources.stoptime
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.todayIn
import moe.lava.banksia.core.model.StopTimeDated
import kotlin.time.Clock
internal class StopTimeRemoteDataSource(
private val client: HttpClient,
) {
suspend fun getAtStop(
stopId: String,
date: LocalDate? = Clock.System.todayIn(TimeZone.currentSystemDefault()),
): List<StopTimeDated> {
return client.get("stoptimes/by_stop/${stopId}") {
parameter("date", date)
}.body<List<StopTimeDated>>()
}
/*suspend fun get(
stop: String? = null,
trip: String? = null,
day: DayOfWeek? = Clock.System.todayIn(TimeZone.currentSystemDefault()).dayOfWeek,
): List<StopTime> {
return client.get("stoptimes") {
stop?.let { parameter("stop", it) }
trip?.let { parameter("trip", it) }
day?.let { parameter("day", it) }
}.body<List<StopTime>>()
}*/
}

View file

@ -0,0 +1,18 @@
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<Trip> {
return listOf()
}
}

View file

@ -0,0 +1,54 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
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 {
android {
namespace = "moe.lava.banksia.core.room"
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 {
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)
}
}
}
dependencies {
add("kspAndroid", libs.room.compiler)
add("kspIosArm64", libs.room.compiler)
add("kspIosSimulatorArm64", libs.room.compiler)
add("kspJvm", libs.room.compiler)
}

View file

@ -0,0 +1,72 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "e536f5a9b1408377bcc449195169648c",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e536f5a9b1408377bcc449195169648c')"
]
}
}

View file

@ -0,0 +1,477 @@
{
"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')"
]
}
}

View file

@ -0,0 +1,498 @@
{
"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')"
]
}
}

View file

@ -0,0 +1,315 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "83ece554400bb035c267dc2414c23293",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Stop",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lat",
"columnName": "lat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lng",
"columnName": "lng",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hasWheelChairBoarding",
"columnName": "hasWheelChairBoarding",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "platformCode",
"columnName": "platformCode",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Stop_parent",
"unique": false,
"columnNames": [
"parent"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
}
]
},
{
"tableName": "StopTime",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "tripId",
"columnName": "tripId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stopId",
"columnName": "stopId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "arrivalTime",
"columnName": "arrivalTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departureTime",
"columnName": "departureTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "headsign",
"columnName": "headsign",
"affinity": "TEXT"
},
{
"fieldPath": "pickupType",
"columnName": "pickupType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dropOffType",
"columnName": "dropOffType",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"tripId",
"stopId"
]
},
"foreignKeys": [
{
"table": "Trip",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"tripId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Stop",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stopId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "Trip",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "routeId",
"columnName": "routeId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceId",
"columnName": "serviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shapeId",
"columnName": "shapeId",
"affinity": "TEXT"
},
{
"fieldPath": "tripHeadsign",
"columnName": "tripHeadsign",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directionId",
"columnName": "directionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockId",
"columnName": "blockId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "wheelchairAccessible",
"columnName": "wheelchairAccessible",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Trip_routeId",
"unique": false,
"columnNames": [
"routeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
}
],
"foreignKeys": [
{
"table": "Route",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"routeId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Shape",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"shapeId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '83ece554400bb035c267dc2414c23293')"
]
}
}

View file

@ -0,0 +1,339 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "5a7252ab3bcae4d0d0950024b19ba002",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Stop",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lat",
"columnName": "lat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lng",
"columnName": "lng",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hasWheelChairBoarding",
"columnName": "hasWheelChairBoarding",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "platformCode",
"columnName": "platformCode",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Stop_parent",
"unique": false,
"columnNames": [
"parent"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
}
]
},
{
"tableName": "StopTime",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "tripId",
"columnName": "tripId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stopId",
"columnName": "stopId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "arrivalTime",
"columnName": "arrivalTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departureTime",
"columnName": "departureTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "headsign",
"columnName": "headsign",
"affinity": "TEXT"
},
{
"fieldPath": "pickupType",
"columnName": "pickupType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dropOffType",
"columnName": "dropOffType",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"tripId",
"stopId"
]
},
"foreignKeys": [
{
"table": "Trip",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"tripId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Stop",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stopId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "Trip",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "routeId",
"columnName": "routeId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceId",
"columnName": "serviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shapeId",
"columnName": "shapeId",
"affinity": "TEXT"
},
{
"fieldPath": "tripHeadsign",
"columnName": "tripHeadsign",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directionId",
"columnName": "directionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockId",
"columnName": "blockId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "wheelchairAccessible",
"columnName": "wheelchairAccessible",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Trip_routeId",
"unique": false,
"columnNames": [
"routeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
}
],
"foreignKeys": [
{
"table": "Route",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"routeId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Shape",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"shapeId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "VersionMetadata",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUpdated",
"columnName": "lastUpdated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"type"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5a7252ab3bcae4d0d0950024b19ba002')"
]
}
}

View file

@ -0,0 +1,368 @@
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "4426fd2ccc826d9d9d9021546b105850",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Stop",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lat",
"columnName": "lat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lng",
"columnName": "lng",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hasWheelChairBoarding",
"columnName": "hasWheelChairBoarding",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "platformCode",
"columnName": "platformCode",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Stop_parent",
"unique": false,
"columnNames": [
"parent"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
}
]
},
{
"tableName": "StopTime",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "tripId",
"columnName": "tripId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stopId",
"columnName": "stopId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "arrivalTime",
"columnName": "arrivalTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departureTime",
"columnName": "departureTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "headsign",
"columnName": "headsign",
"affinity": "TEXT"
},
{
"fieldPath": "pickupType",
"columnName": "pickupType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dropOffType",
"columnName": "dropOffType",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"tripId",
"stopId"
]
},
"indices": [
{
"name": "index_StopTime_tripId",
"unique": true,
"columnNames": [
"tripId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
},
{
"name": "index_StopTime_stopId",
"unique": true,
"columnNames": [
"stopId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
}
],
"foreignKeys": [
{
"table": "Trip",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"tripId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Stop",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stopId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "Trip",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "routeId",
"columnName": "routeId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceId",
"columnName": "serviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shapeId",
"columnName": "shapeId",
"affinity": "TEXT"
},
{
"fieldPath": "tripHeadsign",
"columnName": "tripHeadsign",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directionId",
"columnName": "directionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockId",
"columnName": "blockId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "wheelchairAccessible",
"columnName": "wheelchairAccessible",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Trip_shapeId",
"unique": false,
"columnNames": [
"shapeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
},
{
"name": "index_Trip_routeId",
"unique": false,
"columnNames": [
"routeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
}
],
"foreignKeys": [
{
"table": "Route",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"routeId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Shape",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"shapeId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "VersionMetadata",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUpdated",
"columnName": "lastUpdated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"type"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4426fd2ccc826d9d9d9021546b105850')"
]
}
}

View file

@ -0,0 +1,368 @@
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "4426fd2ccc826d9d9d9021546b105850",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Stop",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lat",
"columnName": "lat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lng",
"columnName": "lng",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hasWheelChairBoarding",
"columnName": "hasWheelChairBoarding",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "platformCode",
"columnName": "platformCode",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Stop_parent",
"unique": false,
"columnNames": [
"parent"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
}
]
},
{
"tableName": "StopTime",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "tripId",
"columnName": "tripId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stopId",
"columnName": "stopId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "arrivalTime",
"columnName": "arrivalTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departureTime",
"columnName": "departureTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "headsign",
"columnName": "headsign",
"affinity": "TEXT"
},
{
"fieldPath": "pickupType",
"columnName": "pickupType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dropOffType",
"columnName": "dropOffType",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"tripId",
"stopId"
]
},
"indices": [
{
"name": "index_StopTime_tripId",
"unique": true,
"columnNames": [
"tripId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
},
{
"name": "index_StopTime_stopId",
"unique": true,
"columnNames": [
"stopId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
}
],
"foreignKeys": [
{
"table": "Trip",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"tripId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Stop",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stopId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "Trip",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "routeId",
"columnName": "routeId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceId",
"columnName": "serviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shapeId",
"columnName": "shapeId",
"affinity": "TEXT"
},
{
"fieldPath": "tripHeadsign",
"columnName": "tripHeadsign",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directionId",
"columnName": "directionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockId",
"columnName": "blockId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "wheelchairAccessible",
"columnName": "wheelchairAccessible",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Trip_shapeId",
"unique": false,
"columnNames": [
"shapeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
},
{
"name": "index_Trip_routeId",
"unique": false,
"columnNames": [
"routeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
}
],
"foreignKeys": [
{
"table": "Route",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"routeId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Shape",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"shapeId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "VersionMetadata",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUpdated",
"columnName": "lastUpdated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"type"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4426fd2ccc826d9d9d9021546b105850')"
]
}
}

View file

@ -0,0 +1,368 @@
{
"formatVersion": 1,
"database": {
"version": 6,
"identityHash": "5f52de4cc0ddbcf02a0d8be4cf4d4cfd",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Stop",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lat",
"columnName": "lat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lng",
"columnName": "lng",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hasWheelChairBoarding",
"columnName": "hasWheelChairBoarding",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "platformCode",
"columnName": "platformCode",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Stop_parent",
"unique": false,
"columnNames": [
"parent"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
}
]
},
{
"tableName": "StopTime",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "tripId",
"columnName": "tripId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stopId",
"columnName": "stopId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "arrivalTime",
"columnName": "arrivalTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departureTime",
"columnName": "departureTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "headsign",
"columnName": "headsign",
"affinity": "TEXT"
},
{
"fieldPath": "pickupType",
"columnName": "pickupType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dropOffType",
"columnName": "dropOffType",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"tripId",
"stopId"
]
},
"indices": [
{
"name": "index_StopTime_tripId",
"unique": false,
"columnNames": [
"tripId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
},
{
"name": "index_StopTime_stopId",
"unique": false,
"columnNames": [
"stopId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
}
],
"foreignKeys": [
{
"table": "Trip",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"tripId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Stop",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stopId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "Trip",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "routeId",
"columnName": "routeId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceId",
"columnName": "serviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shapeId",
"columnName": "shapeId",
"affinity": "TEXT"
},
{
"fieldPath": "tripHeadsign",
"columnName": "tripHeadsign",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directionId",
"columnName": "directionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockId",
"columnName": "blockId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "wheelchairAccessible",
"columnName": "wheelchairAccessible",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Trip_shapeId",
"unique": false,
"columnNames": [
"shapeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
},
{
"name": "index_Trip_routeId",
"unique": false,
"columnNames": [
"routeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
}
],
"foreignKeys": [
{
"table": "Route",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"routeId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Shape",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"shapeId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "VersionMetadata",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUpdated",
"columnName": "lastUpdated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"type"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5f52de4cc0ddbcf02a0d8be4cf4d4cfd')"
]
}
}

View file

@ -0,0 +1,415 @@
{
"formatVersion": 1,
"database": {
"version": 7,
"identityHash": "15c94df0a62438ff28d451c074c94c59",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Service",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `days` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "days",
"columnName": "days",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "start",
"columnName": "start",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "end",
"columnName": "end",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Service_days",
"unique": false,
"columnNames": [
"days"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Service_days` ON `${TABLE_NAME}` (`days`)"
}
]
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Stop",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lat",
"columnName": "lat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lng",
"columnName": "lng",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hasWheelChairBoarding",
"columnName": "hasWheelChairBoarding",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "platformCode",
"columnName": "platformCode",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Stop_parent",
"unique": false,
"columnNames": [
"parent"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
}
]
},
{
"tableName": "StopTime",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "tripId",
"columnName": "tripId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stopId",
"columnName": "stopId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "arrivalTime",
"columnName": "arrivalTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departureTime",
"columnName": "departureTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "headsign",
"columnName": "headsign",
"affinity": "TEXT"
},
{
"fieldPath": "pickupType",
"columnName": "pickupType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dropOffType",
"columnName": "dropOffType",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"tripId",
"stopId"
]
},
"indices": [
{
"name": "index_StopTime_tripId",
"unique": false,
"columnNames": [
"tripId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
},
{
"name": "index_StopTime_stopId",
"unique": false,
"columnNames": [
"stopId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
}
],
"foreignKeys": [
{
"table": "Trip",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"tripId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Stop",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stopId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "Trip",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "routeId",
"columnName": "routeId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceId",
"columnName": "serviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shapeId",
"columnName": "shapeId",
"affinity": "TEXT"
},
{
"fieldPath": "tripHeadsign",
"columnName": "tripHeadsign",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directionId",
"columnName": "directionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockId",
"columnName": "blockId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "wheelchairAccessible",
"columnName": "wheelchairAccessible",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Trip_shapeId",
"unique": false,
"columnNames": [
"shapeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
},
{
"name": "index_Trip_routeId",
"unique": false,
"columnNames": [
"routeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
}
],
"foreignKeys": [
{
"table": "Route",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"routeId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Shape",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"shapeId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "VersionMetadata",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUpdated",
"columnName": "lastUpdated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"type"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '15c94df0a62438ff28d451c074c94c59')"
]
}
}

View file

@ -0,0 +1,426 @@
{
"formatVersion": 1,
"database": {
"version": 8,
"identityHash": "6e0f07bf1af88b2e37b5ad7c38a3fb2a",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Service",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `days` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "days",
"columnName": "days",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "start",
"columnName": "start",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "end",
"columnName": "end",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Service_days",
"unique": false,
"columnNames": [
"days"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Service_days` ON `${TABLE_NAME}` (`days`)"
}
]
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Stop",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lat",
"columnName": "lat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lng",
"columnName": "lng",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hasWheelChairBoarding",
"columnName": "hasWheelChairBoarding",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "platformCode",
"columnName": "platformCode",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Stop_parent",
"unique": false,
"columnNames": [
"parent"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
}
]
},
{
"tableName": "StopTime",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "tripId",
"columnName": "tripId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stopId",
"columnName": "stopId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "arrivalTime",
"columnName": "arrivalTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departureTime",
"columnName": "departureTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "headsign",
"columnName": "headsign",
"affinity": "TEXT"
},
{
"fieldPath": "pickupType",
"columnName": "pickupType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dropOffType",
"columnName": "dropOffType",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"tripId",
"stopId"
]
},
"indices": [
{
"name": "index_StopTime_tripId",
"unique": false,
"columnNames": [
"tripId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
},
{
"name": "index_StopTime_stopId",
"unique": false,
"columnNames": [
"stopId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
}
],
"foreignKeys": [
{
"table": "Trip",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"tripId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Stop",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stopId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "Trip",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`serviceId`) REFERENCES `Service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "routeId",
"columnName": "routeId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceId",
"columnName": "serviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shapeId",
"columnName": "shapeId",
"affinity": "TEXT"
},
{
"fieldPath": "tripHeadsign",
"columnName": "tripHeadsign",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directionId",
"columnName": "directionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockId",
"columnName": "blockId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "wheelchairAccessible",
"columnName": "wheelchairAccessible",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Trip_shapeId",
"unique": false,
"columnNames": [
"shapeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
},
{
"name": "index_Trip_routeId",
"unique": false,
"columnNames": [
"routeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
}
],
"foreignKeys": [
{
"table": "Route",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"routeId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Service",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serviceId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Shape",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"shapeId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "VersionMetadata",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUpdated",
"columnName": "lastUpdated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"type"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6e0f07bf1af88b2e37b5ad7c38a3fb2a')"
]
}
}

View file

@ -0,0 +1,426 @@
{
"formatVersion": 1,
"database": {
"version": 9,
"identityHash": "6e0f07bf1af88b2e37b5ad7c38a3fb2a",
"entities": [
{
"tableName": "Route",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` INTEGER NOT NULL, `number` TEXT, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "number",
"columnName": "number",
"affinity": "TEXT"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Service",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `days` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "days",
"columnName": "days",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "start",
"columnName": "start",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "end",
"columnName": "end",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Service_days",
"unique": false,
"columnNames": [
"days"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Service_days` ON `${TABLE_NAME}` (`days`)"
}
]
},
{
"tableName": "Shape",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `path` BLOB NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "Stop",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `parent` TEXT NOT NULL, `hasWheelChairBoarding` INTEGER NOT NULL, `level` TEXT NOT NULL, `platformCode` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lat",
"columnName": "lat",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lng",
"columnName": "lng",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hasWheelChairBoarding",
"columnName": "hasWheelChairBoarding",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "platformCode",
"columnName": "platformCode",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Stop_parent",
"unique": false,
"columnNames": [
"parent"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Stop_parent` ON `${TABLE_NAME}` (`parent`)"
}
]
},
{
"tableName": "StopTime",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tripId` TEXT NOT NULL, `stopId` TEXT NOT NULL, `arrivalTime` INTEGER NOT NULL, `departureTime` INTEGER NOT NULL, `headsign` TEXT, `pickupType` INTEGER NOT NULL, `dropOffType` INTEGER NOT NULL, PRIMARY KEY(`tripId`, `stopId`), FOREIGN KEY(`tripId`) REFERENCES `Trip`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stopId`) REFERENCES `Stop`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "tripId",
"columnName": "tripId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stopId",
"columnName": "stopId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "arrivalTime",
"columnName": "arrivalTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departureTime",
"columnName": "departureTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "headsign",
"columnName": "headsign",
"affinity": "TEXT"
},
{
"fieldPath": "pickupType",
"columnName": "pickupType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dropOffType",
"columnName": "dropOffType",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"tripId",
"stopId"
]
},
"indices": [
{
"name": "index_StopTime_tripId",
"unique": false,
"columnNames": [
"tripId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_tripId` ON `${TABLE_NAME}` (`tripId`)"
},
{
"name": "index_StopTime_stopId",
"unique": false,
"columnNames": [
"stopId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_StopTime_stopId` ON `${TABLE_NAME}` (`stopId`)"
}
],
"foreignKeys": [
{
"table": "Trip",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"tripId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Stop",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"stopId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "Trip",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `routeId` TEXT NOT NULL, `serviceId` TEXT NOT NULL, `shapeId` TEXT, `tripHeadsign` TEXT NOT NULL, `directionId` TEXT NOT NULL, `blockId` TEXT NOT NULL, `wheelchairAccessible` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`routeId`) REFERENCES `Route`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`serviceId`) REFERENCES `Service`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`shapeId`) REFERENCES `Shape`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "routeId",
"columnName": "routeId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serviceId",
"columnName": "serviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shapeId",
"columnName": "shapeId",
"affinity": "TEXT"
},
{
"fieldPath": "tripHeadsign",
"columnName": "tripHeadsign",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directionId",
"columnName": "directionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "blockId",
"columnName": "blockId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "wheelchairAccessible",
"columnName": "wheelchairAccessible",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_Trip_shapeId",
"unique": false,
"columnNames": [
"shapeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_shapeId` ON `${TABLE_NAME}` (`shapeId`)"
},
{
"name": "index_Trip_routeId",
"unique": false,
"columnNames": [
"routeId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Trip_routeId` ON `${TABLE_NAME}` (`routeId`)"
}
],
"foreignKeys": [
{
"table": "Route",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"routeId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Service",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serviceId"
],
"referencedColumns": [
"id"
]
},
{
"table": "Shape",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"shapeId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "VersionMetadata",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`type`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUpdated",
"columnName": "lastUpdated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"type"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6e0f07bf1af88b2e37b5ad7c38a3fb2a')"
]
}
}

View file

@ -0,0 +1,21 @@
package moe.lava.banksia.core.room
import android.content.Context
import androidx.room.Room
import androidx.room.RoomDatabase
import org.koin.core.parameter.ParametersHolder
import org.koin.core.scope.Scope
class AndroidDatabaseBuilder(val ctx: Context) : PlatformDatabaseBuilder {
override fun getBuilder(): RoomDatabase.Builder<Database> {
val appContext = ctx.applicationContext
val dbFile = appContext.getDatabasePath("room.db")
return Room.databaseBuilder<Database>(
context = appContext,
name = dbFile.absolutePath
)
}
}
internal actual fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder =
AndroidDatabaseBuilder(get())

View file

@ -0,0 +1,83 @@
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<Database>) =
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")
}
}

View file

@ -0,0 +1,26 @@
package moe.lava.banksia.core.room
import androidx.room.RoomDatabase
import org.koin.core.parameter.ParametersHolder
import org.koin.core.scope.Scope
import org.koin.dsl.module
val roomDiModule = module {
single { provideDatabaseBuilder(it) }
single { Database.build(get<PlatformDatabaseBuilder>().getBuilder()) }
single { get<Database>().versionMetadataDao }
single { get<Database>().routeDao }
single { get<Database>().serviceDao }
single { get<Database>().serviceExceptionDao }
single { get<Database>().shapeDao }
single { get<Database>().stopDao }
single { get<Database>().stopTimeDao }
single { get<Database>().tripDao }
}
internal interface PlatformDatabaseBuilder {
fun getBuilder(): RoomDatabase.Builder<Database>
}
internal expect fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder

View file

@ -0,0 +1,12 @@
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
}

View file

@ -0,0 +1,43 @@
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()
}
}

View file

@ -0,0 +1,58 @@
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<RouteEntity>
@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<StopEntity>
// 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<StopEntity>
}

View file

@ -0,0 +1,29 @@
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<ServiceEntity>
@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()
}

View file

@ -0,0 +1,29 @@
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<ServiceExceptionEntity>
@Query("SELECT * FROM ServiceException WHERE serviceId == :id")
suspend fun get(id: String): List<ServiceExceptionEntity>
@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()
}

View file

@ -0,0 +1,26 @@
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()
}

View file

@ -0,0 +1,42 @@
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<StopEntity>
@Query("""
SELECT * FROM Stop
WHERE platformCode <> ""
AND parent == ""
""")
suspend fun getAllParentless(): List<StopEntity>
@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<String>): List<StopEntity>
@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<String>, parent: String)
}

View file

@ -0,0 +1,46 @@
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<StopTimeEntity>
@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<String>): List<StopTimeEntity>
@Query("SELECT * FROM StopTime WHERE stopId == :stopId")
suspend fun getForStop(stopId: String): List<StopTimeEntity>
@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<StopTimeEntity>
@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()
}

View file

@ -0,0 +1,32 @@
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<TripEntity>
@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<TripEntity>
@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()
}

View file

@ -0,0 +1,27 @@
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<VersionMetadataEntity>
@Insert(onConflict = REPLACE)
suspend fun update(vararg data: VersionMetadataEntity)
suspend fun update(vararg data: Pair<String, Long>) {
update(*data.map { (type, lastUpdated) -> VersionMetadataEntity(type, lastUpdated) }.toTypedArray())
}
suspend fun update(lastUpdated: Long, types: Collection<String>) {
update(*types.map { VersionMetadataEntity(it, lastUpdated) }.toTypedArray())
}
}

View file

@ -0,0 +1,18 @@
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)

View file

@ -0,0 +1,31 @@
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(),
)

View file

@ -0,0 +1,28 @@
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,
)

View file

@ -0,0 +1,19 @@
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)

View file

@ -0,0 +1,36 @@
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)

View file

@ -0,0 +1,53 @@
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,
)

View file

@ -0,0 +1,49 @@
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)

View file

@ -0,0 +1,19 @@
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)

View file

@ -0,0 +1,14 @@
package moe.lava.banksia.core.room
import androidx.room.RoomDatabase
import org.koin.core.parameter.ParametersHolder
import org.koin.core.scope.Scope
class IosDatabaseBuilder() : PlatformDatabaseBuilder {
override fun getBuilder(): RoomDatabase.Builder<Database> {
TODO("Not yet implemented")
}
}
internal actual fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder =
IosDatabaseBuilder()

View file

@ -0,0 +1,19 @@
package moe.lava.banksia.core.room
import androidx.room.Room
import androidx.room.RoomDatabase
import org.koin.core.parameter.ParametersHolder
import org.koin.core.scope.Scope
import java.io.File
class JvmDatabaseBuilder() : PlatformDatabaseBuilder {
override fun getBuilder(): RoomDatabase.Builder<Database> {
val dbFile = File("./data/room.db")
return Room.databaseBuilder<Database>(
name = dbFile.absolutePath,
)
}
}
internal actual fun Scope.provideDatabaseBuilder(p: ParametersHolder): PlatformDatabaseBuilder =
JvmDatabaseBuilder()

View file

@ -0,0 +1,12 @@
package moe.lava.banksia.core.util
import android.util.Log
actual fun log(tag: String, msg: String) {
Log.i(tag, msg)
}
actual fun error(tag: String, msg: String, throwable: Throwable?) {
Log.e(tag, msg)
throwable?.let { Log.e(tag, it.stackTraceToString()) }
}

View file

@ -0,0 +1,12 @@
package moe.lava.banksia
object Constants {
const val devid: String = ""
const val key: String = ""
const val opendataKey: String = ""
const val serverUrl: String = "https://banksia.lava.moe/api/"
// TODO
const val devMode: Boolean = false
const val updateKey: String = ""
const val protomapsKey: String = ""
}

View file

@ -0,0 +1,58 @@
package moe.lava.banksia.core.model
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalTime
import kotlinx.datetime.atTime
import kotlinx.datetime.plus
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import moe.lava.banksia.core.model.FutureTime.Companion.asInt
@Serializable(FutureTimeSerialiser::class)
data class FutureTime(
val dayOffset: Boolean,
val time: LocalTime,
) {
companion object {
fun from(hour: Int, minute: Int, second: Int): FutureTime {
var nHour = hour
val nextDay = hour >= 24
if (nextDay)
nHour -= 24
val time = LocalTime(nHour, minute, second)
return FutureTime(nextDay, time)
}
fun FutureTime.asInt() =
trueHour * 3600 + minute * 60 + second
fun fromInt(int: Int) = from(
int / 3600,
(int / 60) % 60,
int % 60,
)
}
val hour = time.hour
val minute = time.minute
val second = time.second
val trueHour = time.hour + (if (dayOffset) 24 else 0)
fun atDate(date: LocalDate) = date
.let { if (dayOffset) date.plus(1, DateTimeUnit.DAY) else date }
.atTime(time)
}
object FutureTimeSerialiser: KSerializer<FutureTime> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor(FutureTimeSerialiser::class.qualifiedName!!, PrimitiveKind.INT)
override fun serialize(encoder: Encoder, value: FutureTime) = encoder.encodeInt(value.asInt())
override fun deserialize(decoder: Decoder) = FutureTime.fromInt(decoder.decodeInt())
}

View file

@ -0,0 +1,11 @@
package moe.lava.banksia.core.model
import kotlinx.serialization.Serializable
@Serializable
data class Route(
val id: String,
val type: RouteType,
val number: String?,
val name: String,
)

View file

@ -0,0 +1,20 @@
package moe.lava.banksia.core.model
import kotlinx.serialization.Serializable
@Serializable
enum class RouteType(val value: Int) {
MetroTrain(2),
MetroTram(3),
MetroBus(4),
RegionalTrain(1),
RegionalCoach(5),
RegionalBus(6),
SkyBus(11),
Interstate(10),
;
companion object {
fun from(value: Int) = entries.first { it.value == value }
}
}

View file

@ -0,0 +1,5 @@
package moe.lava.banksia.core.model
data class Run(
val ref: String,
)

View file

@ -0,0 +1,13 @@
package moe.lava.banksia.core.model
import kotlinx.datetime.DayOfWeek
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
@Serializable
data class Service(
val id: String,
val days: List<DayOfWeek>,
val start: LocalDate,
val end: LocalDate,
)

View file

@ -0,0 +1,11 @@
package moe.lava.banksia.core.model
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
@Serializable
data class ServiceException(
val serviceId: String,
val date: LocalDate,
val type: Int,
)

View file

@ -0,0 +1,12 @@
package moe.lava.banksia.core.model
import kotlinx.serialization.Serializable
import moe.lava.banksia.core.util.Point
typealias ShapePath = List<Point>
@Serializable
data class Shape(
val id: String,
val path: ShapePath,
)

View file

@ -0,0 +1,15 @@
package moe.lava.banksia.core.model
import kotlinx.serialization.Serializable
import moe.lava.banksia.core.util.Point
@Serializable
data class Stop(
val id: String,
val name: String,
val pos: Point,
val parent: String?,
val hasWheelChairBoarding: Boolean,
val level: String,
val platformCode: String,
)

View file

@ -0,0 +1,14 @@
package moe.lava.banksia.core.model
import kotlinx.serialization.Serializable
@Serializable
data class StopTime(
val tripId: String,
val stopId: String,
val arrivalTime: FutureTime,
val departureTime: FutureTime,
val headsign: String?,
val pickupType: Int,
val dropOffType: Int,
)

View file

@ -0,0 +1,26 @@
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,
)

View file

@ -0,0 +1,15 @@
package moe.lava.banksia.core.model
import kotlinx.serialization.Serializable
@Serializable
data class Trip(
val id: String,
val routeId: String,
val service: Service,
val shapeId: String?,
val tripHeadsign: String,
val directionId: String,
val blockId: String,
val wheelchairAccessible: String,
)

View file

@ -0,0 +1,9 @@
package moe.lava.banksia.core.model
import kotlinx.serialization.Serializable
@Serializable
data class VersionMetadata(
val type: String,
val lastUpdated: Long,
)

View file

@ -0,0 +1,10 @@
package moe.lava.banksia.core.util
/** Wraps an arbitrary value, such that equality checks are forced to be done by reference */
class BoxedValue<T>(val value: T) {
operator fun component1() = value
companion object {
fun <T> T.box() = BoxedValue(this)
}
}

View file

@ -0,0 +1,47 @@
package moe.lava.banksia.core.util
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class CacheMap<K, V>(
coroutineScope: CoroutineScope,
val expiryMinutes: Int = 5,
private val innerMap: MutableMap<K, V> = mutableMapOf()
) : MutableMap<K, V> by innerMap {
val keyExpiries = mutableMapOf<K, Int>()
var counter = 0
init {
coroutineScope.launch {
while (true) {
delay(60000)
counter += 1
keyExpiries
.filterValues { expiry -> expiry >= counter }
.keys
.forEach { key ->
innerMap.remove(key)
keyExpiries.remove(key)
}
}
}
}
override fun put(key: K, value: V): V? {
keyExpiries[key] = counter + expiryMinutes + 1
return innerMap.put(key, value)
}
override fun putAll(from: Map<out K, V>) {
keyExpiries.putAll(from.map { it.key to (counter + expiryMinutes + 1) })
innerMap.putAll(from)
}
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() {
error("CacheMap", ".entries accessed, cloning..", IllegalStateException())
return this.entries.toMutableSet()
}
}

View file

@ -0,0 +1,36 @@
package moe.lava.banksia.core.util
import kotlinx.datetime.DayOfWeek
private fun Int.check(other: Int) = (this and other) != 0
fun Int.deserialiseDaysBitflag(): List<DayOfWeek> = buildList {
val days = this@deserialiseDaysBitflag
if (days.check(1))
add(DayOfWeek.MONDAY)
if (days.check(1 shl 1))
add(DayOfWeek.TUESDAY)
if (days.check(1 shl 2))
add(DayOfWeek.WEDNESDAY)
if (days.check(1 shl 3))
add(DayOfWeek.THURSDAY)
if (days.check(1 shl 4))
add(DayOfWeek.FRIDAY)
if (days.check(1 shl 5))
add(DayOfWeek.SATURDAY)
if (days.check(1 shl 6))
add(DayOfWeek.SUNDAY)
}
fun List<DayOfWeek>.serialise(): Int =
this.fold(0) { vl, n ->
vl + when (n) {
DayOfWeek.MONDAY -> 1
DayOfWeek.TUESDAY -> 1 shl 1
DayOfWeek.WEDNESDAY -> 1 shl 2
DayOfWeek.THURSDAY -> 1 shl 3
DayOfWeek.FRIDAY -> 1 shl 4
DayOfWeek.SATURDAY -> 1 shl 5
DayOfWeek.SUNDAY -> 1 shl 6
}
}

View file

@ -0,0 +1,9 @@
package moe.lava.banksia.core.util
fun error(tag: String, throwable: Throwable) = error(tag, "", throwable)
expect fun log(tag: String, msg: String)
expect fun error(tag: String, msg: String, throwable: Throwable? = null)
class LogScope(private val tag: String) {
fun log(msg: String) = log(tag, msg)
}

View file

@ -0,0 +1,64 @@
package moe.lava.banksia.core.util
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.AbstractFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlin.experimental.ExperimentalTypeInference
@OptIn(ExperimentalCoroutinesApi::class)
class LoopFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
private var delayMs: Long = 5000
private var init: (suspend FlowCollector<T>.() -> Unit)? = null
private var waiter: (suspend () -> Unit)? = null
override suspend fun collectSafely(collector: FlowCollector<T>) {
init?.invoke(collector)
while (true) {
waiter?.invoke()
collector.block()
delay(delayMs)
}
}
companion object {
fun <T> Flow<T>.delayFor(delay: Long) = apply {
@Suppress("UnusedFlow")
if (this is LoopFlow)
this.delayMs = delay
else
throw IllegalStateException()
}
fun <T> Flow<T>.initWith(block: suspend FlowCollector<T>.() -> Unit) = apply {
@Suppress("UnusedFlow")
if (this is LoopFlow)
this.init = block
else
throw IllegalStateException()
}
fun <T> Flow<T>.waitFor(waiter: suspend () -> Unit) = apply {
@Suppress("UnusedFlow")
if (this is LoopFlow)
this.waiter = waiter
else
throw IllegalStateException()
}
fun <T> Flow<T>.waitUntilSubscribed(other: MutableStateFlow<*>) = waitFor {
val blocked = other.subscriptionCount.value == 0
if (blocked)
log("LoopFlow", "blocking flow")
other.subscriptionCount.first { it > 0 }
if (blocked)
log("LoopFlow", "unblocking flow")
}
}
}
@OptIn(ExperimentalTypeInference::class)
fun <T> loopFlow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit) = LoopFlow(block)

View file

@ -0,0 +1,6 @@
package moe.lava.banksia.core.util
import kotlinx.serialization.Serializable
@Serializable
data class Point(val lat: Double, val lng: Double)

View file

@ -0,0 +1,255 @@
package moe.lava.banksia.data.ptv
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.HttpSend
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.plugin
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.client.request.url
import io.ktor.client.statement.HttpResponse
import io.ktor.http.appendPathSegments
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.delay
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import moe.lava.banksia.core.Constants
import moe.lava.banksia.core.model.RouteType
import moe.lava.banksia.core.util.LoopFlow.Companion.initWith
import moe.lava.banksia.core.util.error
import moe.lava.banksia.core.util.log
import moe.lava.banksia.core.util.loopFlow
import moe.lava.banksia.data.ptv.structures.PtvDeparture
import moe.lava.banksia.data.ptv.structures.PtvDirection
import moe.lava.banksia.data.ptv.structures.PtvRoute
import moe.lava.banksia.data.ptv.structures.PtvRouteType
import moe.lava.banksia.data.ptv.structures.PtvRouteType.Companion.asPtvType
import moe.lava.banksia.data.ptv.structures.PtvRun
import moe.lava.banksia.data.ptv.structures.PtvStop
import okio.ByteString.Companion.encodeUtf8
import kotlin.random.Random
object Responses {
@Serializable
data class PtvRouteResponse(val route: PtvRoute)
@Serializable
data class PtvRoutesResponse(val routes: List<PtvRoute>)
@Serializable
data class PtvRunsResponse(val runs: List<PtvRun>)
@Serializable
data class PtvStopResponse(val stop: PtvStop)
@Serializable
data class PtvStopsResponse(val stops: List<PtvStop>)
@Serializable
data class PtvDeparturesResponse(val departures: List<PtvDeparture>, val routes: Map<String, PtvRoute>, val directions: Map<String, PtvDirection>)
@Serializable
data class PtvDirectionsResponse(val directions: List<PtvDirection>)
}
suspend inline fun <K, V> MutableMap<K, V>.getOrPutSuspend(key: K, defaultValue: suspend () -> V): V {
if (!containsKey(key))
this[key] = defaultValue()
return this[key]!!
}
class PtvService() {
class PtvCache(
val directions: MutableMap<Pair<Int, Int>, PtvDirection> = mutableMapOf(),
val routes: MutableMap<Int, PtvRoute> = mutableMapOf(),
val runs: MutableMap<String, PtvRun> = mutableMapOf(),
val stops: MutableMap<Int, PtvStop> = mutableMapOf(),
)
val cache = PtvCache()
private val client = HttpClient {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
defaultRequest {
url("https://timetableapi.ptv.vic.gov.au/v3/")
}
}
init {
client.plugin(HttpSend).intercept { req ->
req.parameter("devid", Constants.devid)
@OptIn(ExperimentalStdlibApi::class)
req.parameter("nonce", Random.nextBytes(6).toHexString())
val fullPath = req.url.build().encodedPathAndQuery
val hash = fullPath.encodeUtf8().hmacSha1(Constants.key.encodeUtf8()).hex()
req.parameter("signature", hash)
log("ktor.intercept", req.url.build().encodedPathAndQuery)
execute(req)
}
}
suspend fun HttpClient.safeGet(
urlString: String? = null,
retries: Int = 1,
block: (HttpRequestBuilder.() -> Unit)? = null
): HttpResponse =
runCatching {
get {
urlString?.let { url(it) }
block?.invoke(this)
}
}.getOrElse { e ->
error("PtvService", "Fetch error occurred (attempt $retries / 3), retrying in 5000ms...", e)
if (retries >= 3)
throw e
delay(5000)
safeGet(urlString, retries + 1, block)
}
suspend fun route(id: Int, includeGeopath: Boolean = false): PtvRoute {
val cached = cache.routes[id]
// TODO: im braindead so clean this up later
if (cached != null && (!includeGeopath || (includeGeopath && cached.geopath.isNotEmpty())))
return cached
return client
.safeGet("routes") {
url {
appendPathSegments(id.toString())
parameters.append("include_geopath", if (includeGeopath) "true" else "false")
}
}
.body<Responses.PtvRouteResponse>()
.route
.also { cache.routes[it.routeId] = it }
}
suspend fun routes(): List<PtvRoute> {
val cached = cache.routes
if (cached.isEmpty()) {
client
.safeGet("routes")
.body<Responses.PtvRoutesResponse>()
.routes
.forEach { route ->
cached[route.routeId] = route
}
}
return cached.values.toList()
}
fun runFlow(ref: String, firstWithCache: Boolean = false) =
loopFlow {
client
.safeGet {
url {
appendPathSegments("runs", ref)
}
}
.body<Responses.PtvRunsResponse>()
.runs
.also { it.forEach { run -> cache.runs[run.runRef] = run } }
.let { emit(it[0]) }
}.initWith {
cache.runs[ref]?.let {
if (firstWithCache)
emit(it)
}
}
fun runsFlow(routeId: Int) =
loopFlow {
client
.safeGet {
url {
appendPathSegments("runs", "route", routeId.toString())
parameter("expand", "VehiclePosition")
}
}
.body<Responses.PtvRunsResponse>()
.runs
.also { it.forEach { run -> cache.runs[run.runRef] = run } }
.let { emit(it) }
}
suspend fun stopsByRoute(routeId: Int, routeType: PtvRouteType): List<PtvStop> =
client
.safeGet("stops") {
url {
appendPathSegments(
"route", routeId.toString(),
"route_type", routeType.ordinal.toString(),
)
}
}
.body<Responses.PtvStopsResponse>()
.stops
.also { it.forEach { stop -> cache.stops[stop.stopId] = stop } }
suspend fun stop(routeType: PtvRouteType, stopId: Int): PtvStop =
cache.stops.getOrPutSuspend(stopId) {
client
.safeGet {
url {
appendPathSegments(
"stops", stopId.toString(),
"route_type", routeType.ordinal.toString(),
)
}
}
.body<Responses.PtvStopResponse>()
.stop
}
suspend fun directionsByRoute(routeId: Int): List<PtvDirection> =
client
.safeGet("directions") {
url {
appendPathSegments("route", routeId.toString())
}
}
.body<Responses.PtvDirectionsResponse>()
.directions
suspend fun direction(directionId: Int, routeId: Int): PtvDirection {
if (!cache.directions.containsKey(directionId to routeId)) {
val directions = directionsByRoute(routeId)
for (direction in directions)
cache.directions[direction.directionId to direction.routeId] = direction
}
return cache.directions[directionId to routeId]!!
}
suspend fun departures(routeType: RouteType, stopId: String): Responses.PtvDeparturesResponse =
client
.safeGet ("departures") {
url {
appendPathSegments(
"route_type", routeType.asPtvType().ordinal.toString(),
"stop", stopId,
)
parameter("expand", "Route")
parameter("expand", "Direction")
parameter("gtfs", "true")
}
}.body()
suspend fun departures(routeType: PtvRouteType, stopId: Int): Responses.PtvDeparturesResponse =
client
.safeGet ("departures") {
url {
appendPathSegments(
"route_type", routeType.ordinal.toString(),
"stop", stopId.toString(),
)
parameter("expand", "Route")
parameter("expand", "Direction")
}
}.body()
}

View file

@ -0,0 +1,12 @@
package moe.lava.banksia.data.ptv.structures
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PtvDeparture(
@SerialName("scheduled_departure_utc") val scheduledDepartureUtc: String,
@SerialName("estimated_departure_utc") val estimatedDepartureUtc: String?,
@SerialName("direction_id") val directionId: Int,
@SerialName("route_id") val routeId: Int,
)

View file

@ -0,0 +1,11 @@
package moe.lava.banksia.data.ptv.structures
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PtvDirection(
@SerialName("direction_id") val directionId: Int,
@SerialName("direction_name") val directionName: String,
@SerialName("route_id") val routeId: Int,
)

View file

@ -0,0 +1,13 @@
package moe.lava.banksia.data.ptv.structures
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PtvGeopath(
@SerialName("direction_id") val directionId: Int,
@SerialName("valid_from") val validFrom: String,
@SerialName("valid_to") val validTo: String,
@SerialName("paths") val paths: List<String>,
)

View file

@ -0,0 +1,26 @@
package moe.lava.banksia.data.ptv.structures
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import moe.lava.banksia.core.model.RouteType
@Serializable
data class PtvRoute(
@SerialName("route_type") val routeType: PtvRouteType,
@SerialName("route_id") val routeId: Int,
@SerialName("route_number") val routeNumber: String,
@SerialName("route_name") val routeName: String,
@SerialName("route_gtfs_id") val routeGtfsId: String,
@SerialName("geopath") val geopath: List<PtvGeopath>,
) {
fun gtfsSubType(): RouteType =
RouteType.entries.first { routeGtfsId.startsWith(it.value.toString() + "-") }
fun getShortFullName(): String {
var res = ""
if (this.routeNumber != "")
res += this.routeNumber + " - "
res += this.routeName.split(" via")[0]
return res
}
}

View file

@ -0,0 +1,50 @@
package moe.lava.banksia.data.ptv.structures
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import moe.lava.banksia.core.model.RouteType
object PtvRouteTypeSerialiser : KSerializer<PtvRouteType> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(
PtvRouteType::class.qualifiedName!!,
PrimitiveKind.INT)
override fun serialize(encoder: Encoder, value: PtvRouteType) {
encoder.encodeInt(value.ordinal)
}
override fun deserialize(decoder: Decoder): PtvRouteType {
val index = decoder.decodeInt()
return PtvRouteType.entries[index]
}
}
@Serializable(with = PtvRouteTypeSerialiser::class)
enum class PtvRouteType {
TRAIN,
TRAM,
BUS,
VLINE,
NIGHT_BUS,
;
companion object {
fun fromModel(type: RouteType) = when (type) {
RouteType.MetroTrain -> TRAIN
RouteType.MetroTram -> TRAM
RouteType.MetroBus -> BUS
RouteType.RegionalTrain -> VLINE
RouteType.RegionalCoach -> BUS
RouteType.RegionalBus -> BUS
RouteType.SkyBus -> BUS
RouteType.Interstate -> TRAIN
}
fun RouteType.asPtvType() = fromModel(this)
}
}

View file

@ -0,0 +1,72 @@
package moe.lava.banksia.data.ptv.structures
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlin.time.Instant
// Some datetimes are in local time (no timezone), observed on bus vehicle positions,
// and some datetimes are in UTC, observed on train vehicle positions. We need to handle
// both cases.
private object CustomInstantSerialiser : KSerializer<Instant> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor(
CustomInstantSerialiser::class.qualifiedName!!,
PrimitiveKind.STRING,
)
override fun serialize(
encoder: Encoder,
value: Instant
) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): Instant {
val str = decoder.decodeString()
return runCatching {
Instant.parse(str)
}.getOrElse {
LocalDateTime.parse(str).toInstant(TimeZone.currentSystemDefault())
}
}
}
@Serializable
data class PtvVehiclePosition(
val latitude: Double,
val longitude: Double,
val easting: Double?,
val northing: Double?,
val direction: String?,
val bearing: Double?,
val supplier: String?,
@Serializable(CustomInstantSerialiser::class)
@SerialName("datetime_utc")
val datetimeUtc: Instant?,
@Serializable(CustomInstantSerialiser::class)
@SerialName("expiry_time")
val expiryTime: Instant?,
)
@Serializable
data class PtvRun(
@SerialName("run_ref") val runRef: String,
@SerialName("route_id") val routeId: Int,
@SerialName("route_type") val routeType: PtvRouteType,
@SerialName("final_stop_id") val finalStopId: Int,
@SerialName("destination_name") val destinationName: String,
@SerialName("direction_id") val directionId: Int,
@SerialName("status") val status: String,
@SerialName("vehicle_position") val vehiclePosition: PtvVehiclePosition?,
)

View file

@ -0,0 +1,13 @@
package moe.lava.banksia.data.ptv.structures
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PtvStop(
@SerialName("stop_id") val stopId: Int,
@SerialName("stop_name") val stopName: String,
@SerialName("stop_latitude") val stopLatitude: Double?,
@SerialName("stop_longitude") val stopLongitude: Double?,
@SerialName("route_type") val routeType: PtvRouteType,
)

View file

@ -0,0 +1,9 @@
package moe.lava.banksia.core.util
actual fun log(tag: String, msg: String) {
TODO("Not yet implemented")
}
actual fun error(tag: String, msg: String, throwable: Throwable?) {
TODO("Not yet implemented")
}

View file

@ -0,0 +1,10 @@
package moe.lava.banksia.core.util
actual fun log(tag: String, msg: String) {
println("[$tag] $msg")
}
actual fun error(tag: String, msg: String, throwable: Throwable?) {
println("[$tag] $msg")
throwable?.let { println(it.stackTraceToString()) }
}