mirror of
https://github.com/2martens/tsw-timetable.git
synced 2026-10-17 16:06:51 +02:00
Added code to schedule fetch tasks
This commit is contained in:
+4
-3
@@ -1,5 +1,7 @@
|
||||
package de.twomartens.timetable.bahnApi.model.db
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.model.Hour
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.annotation.CreatedDate
|
||||
import org.springframework.data.annotation.Id
|
||||
@@ -9,14 +11,13 @@ import org.springframework.data.mongodb.core.mapping.Document
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
|
||||
@Document
|
||||
@CompoundIndex(def = "{'eva': 1, 'fetchedDate': 1, 'fetchedTime': 1", unique = true)
|
||||
data class ScheduledFetchTask(
|
||||
var eva: Int,
|
||||
var eva: Eva,
|
||||
var fetchedDate: LocalDate,
|
||||
var fetchedTime: LocalTime,
|
||||
var fetchedTime: Hour,
|
||||
var scheduledExecutionDateTime: LocalDateTime
|
||||
) {
|
||||
@Id
|
||||
|
||||
+5
-2
@@ -4,6 +4,8 @@ import de.twomartens.timetable.bahnApi.model.dto.BahnStation
|
||||
import de.twomartens.timetable.bahnApi.model.dto.BahnStations
|
||||
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
|
||||
import de.twomartens.timetable.bahnApi.property.BahnApiProperties
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.model.Hour
|
||||
import org.springframework.http.HttpEntity
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpMethod
|
||||
@@ -31,13 +33,14 @@ class BahnApiService(
|
||||
return body?.stations ?: listOf()
|
||||
}
|
||||
|
||||
fun fetchTimetable(eva: Int, date: LocalDate, hour: LocalTime): BahnTimetable {
|
||||
fun fetchTimetable(eva: Eva, date: LocalDate, hour: Hour): BahnTimetable {
|
||||
val requestEntity = buildRequestEntity<BahnTimetable>()
|
||||
val dateFormatter = DateTimeFormatter.ofPattern("yyMMdd")
|
||||
val timeFormatter = DateTimeFormatter.ofPattern("HH")
|
||||
val time = LocalTime.of(hour.value, 0)
|
||||
val response = restTemplate.exchange(
|
||||
"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/" +
|
||||
"${eva}/${date.format(dateFormatter)}/${hour.format(timeFormatter)}",
|
||||
"${eva}/${date.format(dateFormatter)}/${time.format(timeFormatter)}",
|
||||
HttpMethod.GET,
|
||||
requestEntity,
|
||||
BahnTimetable::class.java
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.twomartens.timetable.model
|
||||
|
||||
@JvmInline
|
||||
value class Eva(val value: Int) {
|
||||
init {
|
||||
require(value in 8000000..8999999) {
|
||||
"Eva number must start with an 8 and have 7 digits"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.twomartens.timetable.model
|
||||
|
||||
@JvmInline
|
||||
value class Hour(val value: Int) {
|
||||
init {
|
||||
require(value in 0..23) {
|
||||
"Hour must be between 0 and 23"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.twomartens.timetable.model
|
||||
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@JvmInline
|
||||
value class HourAtDay(val value: LocalDateTime) {
|
||||
init {
|
||||
require(value.minute == 0 && value.second == 0 && value.nano == 0) {
|
||||
"DateTime must only include an hour and the date."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package de.twomartens.timetable.model
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.db.ScheduledFetchTask
|
||||
import java.time.LocalDate
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
private const val SECONDS_BETWEEN_TASKS = 2
|
||||
|
||||
class Tasks {
|
||||
private val _tasks: MutableMap<HourAtDay, MutableList<ScheduledFetchTask>> = mutableMapOf()
|
||||
var tasks: List<ScheduledFetchTask> = listOf()
|
||||
get() {
|
||||
return _tasks.flatMap {
|
||||
it.value
|
||||
}
|
||||
}
|
||||
set(newTasks) {
|
||||
_tasks.clear()
|
||||
newTasks.forEach { addTask(it) }
|
||||
field = newTasks
|
||||
}
|
||||
|
||||
fun createAndAddTask(eva: Eva, date: LocalDate, hour: Hour): ScheduledFetchTask {
|
||||
val hourAtDay = HourAtDay(date.atTime(hour.value, 0))
|
||||
val tasksForHourAtDay = _tasks.computeIfAbsent(hourAtDay) { mutableListOf() }
|
||||
val secondInHourToExecute = tasksForHourAtDay.size * SECONDS_BETWEEN_TASKS
|
||||
val fetchDateTime = hourAtDay.value.plusSeconds(secondInHourToExecute.toLong())
|
||||
val task = ScheduledFetchTask(eva, date, hour, fetchDateTime)
|
||||
|
||||
tasksForHourAtDay.add(task)
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
private fun addTask(scheduledFetchTask: ScheduledFetchTask) {
|
||||
val scheduledTime = scheduledFetchTask.scheduledExecutionDateTime
|
||||
val hourAtDay = HourAtDay(scheduledTime.truncatedTo(ChronoUnit.HOURS))
|
||||
val tasksForHourAtDay = _tasks.computeIfAbsent(hourAtDay) { mutableListOf() }
|
||||
|
||||
tasksForHourAtDay.add(scheduledFetchTask)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
data class Platform(
|
||||
var name: String,
|
||||
var sections: MutableList<Section>
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
data class Section(
|
||||
var name: String
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.annotation.CreatedDate
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.annotation.LastModifiedDate
|
||||
import org.springframework.data.mongodb.core.index.Indexed
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
import java.time.Instant
|
||||
|
||||
@Document
|
||||
data class Station(
|
||||
@Indexed(unique = true) var eva: Eva,
|
||||
var name: String,
|
||||
var platforms: List<Platform>
|
||||
) {
|
||||
@Id
|
||||
var id: ObjectId = ObjectId()
|
||||
|
||||
@CreatedDate
|
||||
lateinit var created: Instant
|
||||
|
||||
@LastModifiedDate
|
||||
lateinit var lastModified: Instant
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.annotation.CreatedDate
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.annotation.LastModifiedDate
|
||||
import org.springframework.data.mongodb.core.index.Indexed
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
import java.time.Instant
|
||||
|
||||
@Document
|
||||
data class TswRoute(
|
||||
@Indexed(unique = true) var name: String,
|
||||
/**
|
||||
* Ordered list of stations that represent the order of stations on the route.
|
||||
*
|
||||
* Not every stop is necessarily called at by each train. There may be multiple
|
||||
* parallel paths through a route (like S-Bahn track and long-distance track).
|
||||
* In such cases, the S-Bahn stops are listed as part of the route as they appear
|
||||
* geographically.
|
||||
*
|
||||
* Only the station ids are used here as the duplication would be heavy and
|
||||
* consistency hard to ensure when potentially repeated across various routes.
|
||||
*/
|
||||
var stations: List<Eva>
|
||||
) {
|
||||
@Id
|
||||
var id: ObjectId = ObjectId()
|
||||
|
||||
@CreatedDate
|
||||
lateinit var created: Instant
|
||||
|
||||
@LastModifiedDate
|
||||
lateinit var lastModified: Instant
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
package de.twomartens.timetable.service
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.db.ScheduledFetchTask
|
||||
import de.twomartens.timetable.bahnApi.repository.ScheduledFetchTaskRepository
|
||||
import de.twomartens.timetable.bahnApi.service.BahnApiService
|
||||
import de.twomartens.timetable.bahnApi.service.BahnDatabaseService
|
||||
import de.twomartens.timetable.bahnApi.tasks.DeleteScheduledTask
|
||||
import de.twomartens.timetable.bahnApi.tasks.FetchTimetableTask
|
||||
import de.twomartens.timetable.model.Hour
|
||||
import de.twomartens.timetable.model.Tasks
|
||||
import de.twomartens.timetable.model.db.TswRoute
|
||||
import de.twomartens.timetable.property.TimeProperties
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.LocalDate
|
||||
|
||||
@Service
|
||||
class ScheduledTaskService(
|
||||
@@ -19,8 +25,41 @@ class ScheduledTaskService(
|
||||
private val bahnDatabaseService: BahnDatabaseService,
|
||||
private val threadPoolTaskExecutor: ThreadPoolTaskExecutor
|
||||
) {
|
||||
private val tasks: Tasks = Tasks()
|
||||
|
||||
fun initializeScheduledTasks() {
|
||||
log.info { "Initialize persisted scheduled fetch tasks" }
|
||||
val scheduledTasks = scheduledFetchTaskRepository.findAll()
|
||||
tasks.tasks = scheduledTasks
|
||||
scheduleTasks(scheduledTasks)
|
||||
}
|
||||
|
||||
fun scheduleTimetableFetch(tswRoute: TswRoute, fetchedDate: LocalDate) {
|
||||
log.info {
|
||||
"Schedule timetable fetch: [route ${tswRoute.name}]"
|
||||
}
|
||||
val previousDay = fetchedDate.minusDays(1)
|
||||
val nextDay = fetchedDate.plusDays(1)
|
||||
val newTasks = mutableListOf<ScheduledFetchTask>()
|
||||
tswRoute.stations.forEach {
|
||||
val eva = it
|
||||
var newTask = tasks.createAndAddTask(eva, date = previousDay, hour = Hour(23))
|
||||
newTasks.add(newTask)
|
||||
for (hour in 0..23) {
|
||||
newTask = tasks.createAndAddTask(eva, date = fetchedDate, hour = Hour(hour))
|
||||
newTasks.add(newTask)
|
||||
}
|
||||
for (hour in 0..3) {
|
||||
newTask = tasks.createAndAddTask(eva, date = nextDay, hour = Hour(hour))
|
||||
newTasks.add(newTask)
|
||||
}
|
||||
}
|
||||
|
||||
scheduledFetchTaskRepository.saveAll(newTasks)
|
||||
scheduleTasks(newTasks)
|
||||
}
|
||||
|
||||
private fun scheduleTasks(scheduledTasks: List<ScheduledFetchTask>) {
|
||||
scheduledTasks.forEach {
|
||||
val zonedExecutionTime = it.scheduledExecutionDateTime
|
||||
.atZone(timeProperties.defaultTimeZone)
|
||||
@@ -31,4 +70,8 @@ class ScheduledTaskService(
|
||||
threadPoolTaskScheduler.schedule(deleteTask, zonedExecutionTime.toInstant())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = KotlinLogging.logger {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user