feat: stop times/departures reimpl based on gtfs

This commit is contained in:
Cilly Leang 2026-03-31 20:09:48 +11:00
parent b5f2ec102d
commit 72b9fb2757
Signed by: cilly
GPG key ID: 6500251E087653C9
23 changed files with 1630 additions and 128 deletions

View file

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

View file

@ -1,44 +0,0 @@
package moe.lava.banksia.client.data.stoptime
import moe.lava.banksia.data.ptv.PtvService
import moe.lava.banksia.model.RouteType
import moe.lava.banksia.model.StopTime
class StopTimePtvDataSource(
private val ptvService: PtvService,
) {
suspend fun getForStop(type: RouteType, stopId: String): List<StopTime> {
return listOf()
// val res = ptvService.departures(type, stopId)
// // Map<
// // Pair<DirectionId, RouteId>,
// // Pair<DirectionName, List<DepartureTimes>>
// // >
// val timetable = HashMap<Pair<Int, Int>, Pair<String, MutableList<String>>>()
// res.departures.forEach { dep ->
// val key = Pair(dep.directionId, dep.routeId)
// val direction = ptvService.direction(dep.directionId, dep.routeId)
// val route = res.routes[dep.routeId.toString()]
// val prefix = route?.let { if (it.routeNumber == "") "" else "${it.routeNumber} - " } ?: ""
// val element = timetable.getOrPut(key) { Pair(prefix + direction.directionName, mutableListOf()) }.second
// if (element.size >= 5)
// return@forEach
//
// val date = Instant.parse(dep.estimatedDepartureUtc ?: dep.scheduledDepartureUtc)
// val min = (date - Clock.System.now()).inWholeMinutes
// if (min <= -5)
// return@forEach
// if (min >= 65)
// element.add("${((min + 30.0) / 60.0).toInt()}hr")
// else
// element.add("${min}mn")
// }
//
// val departures = timetable.values.sortedBy { it.first }.map { (name, list) ->
// if (list.isEmpty())
// InfoPanelState.Stop.Departure(name, "No departures")
// else
// InfoPanelState.Stop.Departure(name, list.joinToString(" | "))
// }
}
}

View file

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

View file

@ -0,0 +1,18 @@
package moe.lava.banksia.client.data.trip
import io.ktor.client.HttpClient
import kotlinx.datetime.DayOfWeek
import kotlinx.datetime.TimeZone
import kotlinx.datetime.todayIn
import moe.lava.banksia.model.Trip
import kotlin.time.Clock
class TripRemoteDataSource(
private val client: HttpClient,
) {
suspend fun get(
day: DayOfWeek? = Clock.System.todayIn(TimeZone.currentSystemDefault()).dayOfWeek,
): List<Trip> {
return listOf()
}
}

View file

@ -12,7 +12,8 @@ import moe.lava.banksia.client.data.route.RouteLocalDataSource
import moe.lava.banksia.client.data.route.RouteRemoteDataSource
import moe.lava.banksia.client.data.stop.StopLocalDataSource
import moe.lava.banksia.client.data.stop.StopRemoteDataSource
import moe.lava.banksia.client.data.stoptime.StopTimePtvDataSource
import moe.lava.banksia.client.data.stoptime.StopTimeLocalDataSource
import moe.lava.banksia.client.data.stoptime.StopTimeRemoteDataSource
import moe.lava.banksia.client.repository.RouteRepository
import moe.lava.banksia.client.repository.StopRepository
import moe.lava.banksia.client.repository.StopTimeRepository
@ -48,7 +49,8 @@ val ClientModule = module {
singleOf(::RouteRemoteDataSource)
singleOf(::StopLocalDataSource)
singleOf(::StopRemoteDataSource)
singleOf(::StopTimePtvDataSource)
singleOf(::StopTimeLocalDataSource)
singleOf(::StopTimeRemoteDataSource)
// Repositories
singleOf(::RouteRepository)

View file

@ -1,13 +1,16 @@
package moe.lava.banksia.client.repository
import moe.lava.banksia.client.data.stoptime.StopTimePtvDataSource
import moe.lava.banksia.model.StopTime
import moe.lava.banksia.client.data.stoptime.StopTimeLocalDataSource
import moe.lava.banksia.client.data.stoptime.StopTimeRemoteDataSource
import moe.lava.banksia.model.StopTimeDated
class StopTimeRepository(
private val ptv: StopTimePtvDataSource,
private val local: StopTimeLocalDataSource,
private val remote: StopTimeRemoteDataSource,
) {
// TODO
suspend fun getForStop(id: String): List<StopTime> {
return listOf()
suspend fun getForStop(id: String): List<StopTimeDated> {
return local
.getAtStop(id)
.ifEmpty { remote.getAtStop(id) }
}
}