diff --git a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt index ed4fd25..21e239c 100644 --- a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt +++ b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/GtfsParser.kt @@ -21,12 +21,14 @@ import moe.lava.banksia.Constants import moe.lava.banksia.model.Route import moe.lava.banksia.model.RouteType import moe.lava.banksia.model.Service +import moe.lava.banksia.model.ServiceException import moe.lava.banksia.model.Shape import moe.lava.banksia.model.Stop import moe.lava.banksia.model.StopTime import moe.lava.banksia.model.Trip import moe.lava.banksia.server.gtfs.structures.GtfsRoute import moe.lava.banksia.server.gtfs.structures.GtfsService +import moe.lava.banksia.server.gtfs.structures.GtfsServiceException import moe.lava.banksia.server.gtfs.structures.GtfsShape import moe.lava.banksia.server.gtfs.structures.GtfsStop import moe.lava.banksia.server.gtfs.structures.GtfsStopTime @@ -39,6 +41,7 @@ import kotlin.time.ExperimentalTime sealed class GtfsData { data class RouteChunk(val routes: List) : GtfsData() data class ServiceChunk(val services: List) : GtfsData() + data class ServiceExceptionChunk(val exceptions: List) : GtfsData() data class ShapeChunk(val shapes: List) : GtfsData() data class StopChunk(val stops: List) : GtfsData() data class StopTimeChunk(val stopTimes: List) : GtfsData() @@ -77,7 +80,7 @@ class GtfsParser( .listFiles { it.isDirectory } .flatMap { d -> d.listFiles { f -> f.extension == "txt" }.toList() } .ifEmpty { extractAll(datasetPath) } - .filter { it.parentFile.name == "2" } +// .filter { it.parentFile.name == "2" } } else { extractAll(datasetPath) } @@ -115,6 +118,10 @@ class GtfsParser( } .associateBy { it.id } + files + .filter { it.name == "calendar_dates.txt" } + .forEach { emit(GtfsData.ServiceExceptionChunk(parseServiceExceptions(it))) } + val trips = files .filter { it.name == "trips.txt" } .flatMap { fd -> @@ -207,6 +214,16 @@ class GtfsParser( ) } } + private fun parseServiceExceptions(fd: File) = + fd.parseCsv() + .map { with(it) { + ServiceException( + serviceId = service_id, + date = LocalDate.parse(date, LocalDate.Formats.ISO_BASIC), + type = exception_type, + ) + } } + private fun parseTrips(fd: File, services: Map) = fd.parseCsv() .map { with(it) { diff --git a/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/structures/GtfsServiceException.kt b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/structures/GtfsServiceException.kt new file mode 100644 index 0000000..a31aff0 --- /dev/null +++ b/server/gtfs/src/main/kotlin/moe/lava/banksia/server/gtfs/structures/GtfsServiceException.kt @@ -0,0 +1,11 @@ +package moe.lava.banksia.server.gtfs.structures + +import kotlinx.serialization.Serializable + +@Suppress("PropertyName") +@Serializable +internal data class GtfsServiceException( + val service_id: String, + val date: String, + val exception_type: Int, +) diff --git a/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt b/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt index 8928cda..04ea373 100644 --- a/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt +++ b/server/src/main/kotlin/moe/lava/banksia/server/GtfsImporter.kt @@ -5,6 +5,7 @@ import androidx.room.useWriterConnection import io.ktor.util.logging.Logger import moe.lava.banksia.model.Route import moe.lava.banksia.model.Service +import moe.lava.banksia.model.ServiceException import moe.lava.banksia.model.Shape import moe.lava.banksia.model.Stop import moe.lava.banksia.model.StopTime @@ -25,6 +26,7 @@ class GtfsImporter( transactor.immediateTransaction { database.routeDao.deleteAll() database.serviceDao.deleteAll() + database.serviceExceptionDao.deleteAll() database.shapeDao.deleteAll() database.stopDao.deleteAll() database.stopTimeDao.deleteAll() @@ -34,6 +36,7 @@ class GtfsImporter( when (chunk) { is GtfsData.RouteChunk -> addRoutes(chunk.routes) is GtfsData.ServiceChunk -> addServices(chunk.services) + is GtfsData.ServiceExceptionChunk -> addServiceExceptions(chunk.exceptions) is GtfsData.ShapeChunk -> addShapes(chunk.shapes) is GtfsData.StopChunk -> addStops(chunk.stops) is GtfsData.StopTimeChunk -> addStopTimes(chunk.stopTimes) @@ -67,6 +70,13 @@ class GtfsImporter( log.info("done") } + private suspend fun addServiceExceptions(exceptions: List) { + val dao = database.serviceExceptionDao + log.info("inserting exceptions...") + dao.insertOrReplaceAll(*exceptions.map { it.asEntity() }.toTypedArray()) + log.info("done") + } + private suspend fun addShapes(shapes: List) { val dao = database.shapeDao log.info("inserting shapes...") diff --git a/shared/schemas/moe.lava.banksia.room.Database/10.json b/shared/schemas/moe.lava.banksia.room.Database/10.json new file mode 100644 index 0000000..751e946 --- /dev/null +++ b/shared/schemas/moe.lava.banksia.room.Database/10.json @@ -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')" + ] + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/moe/lava/banksia/di/CommonModules.kt b/shared/src/commonMain/kotlin/moe/lava/banksia/di/CommonModules.kt index 8658342..1a39cfb 100644 --- a/shared/src/commonMain/kotlin/moe/lava/banksia/di/CommonModules.kt +++ b/shared/src/commonMain/kotlin/moe/lava/banksia/di/CommonModules.kt @@ -10,6 +10,7 @@ val CommonModules = module { single { get().versionMetadataDao } single { get().routeDao } single { get().serviceDao } + single { get().serviceExceptionDao } single { get().shapeDao } single { get().stopDao } single { get().stopTimeDao } diff --git a/shared/src/commonMain/kotlin/moe/lava/banksia/model/ServiceException.kt b/shared/src/commonMain/kotlin/moe/lava/banksia/model/ServiceException.kt new file mode 100644 index 0000000..305ede4 --- /dev/null +++ b/shared/src/commonMain/kotlin/moe/lava/banksia/model/ServiceException.kt @@ -0,0 +1,11 @@ +package moe.lava.banksia.model + +import kotlinx.datetime.LocalDate +import kotlinx.serialization.Serializable + +@Serializable +data class ServiceException( + val serviceId: String, + val date: LocalDate, + val type: Int, +) diff --git a/shared/src/commonMain/kotlin/moe/lava/banksia/room/Database.kt b/shared/src/commonMain/kotlin/moe/lava/banksia/room/Database.kt index 89bc24a..31a5579 100644 --- a/shared/src/commonMain/kotlin/moe/lava/banksia/room/Database.kt +++ b/shared/src/commonMain/kotlin/moe/lava/banksia/room/Database.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.IO import moe.lava.banksia.room.converter.RouteTypeConverter import moe.lava.banksia.room.dao.RouteDao import moe.lava.banksia.room.dao.ServiceDao +import moe.lava.banksia.room.dao.ServiceExceptionDao import moe.lava.banksia.room.dao.ShapeDao import moe.lava.banksia.room.dao.StopDao import moe.lava.banksia.room.dao.StopTimeDao @@ -16,6 +17,7 @@ import moe.lava.banksia.room.dao.TripDao import moe.lava.banksia.room.dao.VersionMetadataDao import moe.lava.banksia.room.entity.RouteEntity import moe.lava.banksia.room.entity.ServiceEntity +import moe.lava.banksia.room.entity.ServiceExceptionEntity import moe.lava.banksia.room.entity.ShapeEntity import moe.lava.banksia.room.entity.StopEntity import moe.lava.banksia.room.entity.StopTimeEntity @@ -24,10 +26,11 @@ import moe.lava.banksia.room.entity.VersionMetadataEntity import androidx.room.Database as DatabaseAnnotation @DatabaseAnnotation( - version = 9, + version = 10, entities = [ RouteEntity::class, ServiceEntity::class, + ServiceExceptionEntity::class, ShapeEntity::class, StopEntity::class, StopTimeEntity::class, @@ -37,6 +40,7 @@ import androidx.room.Database as DatabaseAnnotation autoMigrations = [ AutoMigration(from = 1, to = 2), AutoMigration(from = 2, to = 3), + AutoMigration(from = 9, to = 10), ] ) @TypeConverters(RouteTypeConverter::class) @@ -44,6 +48,7 @@ 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 diff --git a/shared/src/commonMain/kotlin/moe/lava/banksia/room/dao/ServiceExceptionDao.kt b/shared/src/commonMain/kotlin/moe/lava/banksia/room/dao/ServiceExceptionDao.kt new file mode 100644 index 0000000..123b0c6 --- /dev/null +++ b/shared/src/commonMain/kotlin/moe/lava/banksia/room/dao/ServiceExceptionDao.kt @@ -0,0 +1,29 @@ +package moe.lava.banksia.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.room.entity.ServiceExceptionEntity + +@Dao +interface ServiceExceptionDao { + @Query("SELECT * FROM ServiceException") + suspend fun getAll(): List + + @Query("SELECT * FROM ServiceException WHERE serviceId == :id") + suspend fun get(id: String): List + + @Insert + suspend fun insertAll(vararg exceptions: ServiceExceptionEntity) + + @Insert(onConflict = REPLACE) + suspend fun insertOrReplaceAll(vararg exceptions: ServiceExceptionEntity) + + @Delete + suspend fun delete(service: ServiceExceptionEntity) + + @Query("DELETE FROM ServiceException") + suspend fun deleteAll() +} diff --git a/shared/src/commonMain/kotlin/moe/lava/banksia/room/dao/StopTimeDao.kt b/shared/src/commonMain/kotlin/moe/lava/banksia/room/dao/StopTimeDao.kt index d5e1744..cd377cc 100644 --- a/shared/src/commonMain/kotlin/moe/lava/banksia/room/dao/StopTimeDao.kt +++ b/shared/src/commonMain/kotlin/moe/lava/banksia/room/dao/StopTimeDao.kt @@ -22,11 +22,13 @@ interface StopTimeDao { suspend fun getForStop(stopId: String): List @Query(""" - SELECT * FROM StopTime + 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 == :stopId + AND StopTime.stopId == :stopId + AND ServiceException.type IS NULL """) suspend fun getForStopDated(stopId: String, days: Int, date: Int): List diff --git a/shared/src/commonMain/kotlin/moe/lava/banksia/room/entity/ServiceExceptionEntity.kt b/shared/src/commonMain/kotlin/moe/lava/banksia/room/entity/ServiceExceptionEntity.kt new file mode 100644 index 0000000..313246d --- /dev/null +++ b/shared/src/commonMain/kotlin/moe/lava/banksia/room/entity/ServiceExceptionEntity.kt @@ -0,0 +1,28 @@ +package moe.lava.banksia.room.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import kotlinx.datetime.LocalDate +import moe.lava.banksia.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, +)