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
|
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.bson.types.ObjectId
|
||||||
import org.springframework.data.annotation.CreatedDate
|
import org.springframework.data.annotation.CreatedDate
|
||||||
import org.springframework.data.annotation.Id
|
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.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.LocalTime
|
|
||||||
|
|
||||||
@Document
|
@Document
|
||||||
@CompoundIndex(def = "{'eva': 1, 'fetchedDate': 1, 'fetchedTime': 1", unique = true)
|
@CompoundIndex(def = "{'eva': 1, 'fetchedDate': 1, 'fetchedTime': 1", unique = true)
|
||||||
data class ScheduledFetchTask(
|
data class ScheduledFetchTask(
|
||||||
var eva: Int,
|
var eva: Eva,
|
||||||
var fetchedDate: LocalDate,
|
var fetchedDate: LocalDate,
|
||||||
var fetchedTime: LocalTime,
|
var fetchedTime: Hour,
|
||||||
var scheduledExecutionDateTime: LocalDateTime
|
var scheduledExecutionDateTime: LocalDateTime
|
||||||
) {
|
) {
|
||||||
@Id
|
@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.BahnStations
|
||||||
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
|
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
|
||||||
import de.twomartens.timetable.bahnApi.property.BahnApiProperties
|
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.HttpEntity
|
||||||
import org.springframework.http.HttpHeaders
|
import org.springframework.http.HttpHeaders
|
||||||
import org.springframework.http.HttpMethod
|
import org.springframework.http.HttpMethod
|
||||||
@@ -31,13 +33,14 @@ class BahnApiService(
|
|||||||
return body?.stations ?: listOf()
|
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 requestEntity = buildRequestEntity<BahnTimetable>()
|
||||||
val dateFormatter = DateTimeFormatter.ofPattern("yyMMdd")
|
val dateFormatter = DateTimeFormatter.ofPattern("yyMMdd")
|
||||||
val timeFormatter = DateTimeFormatter.ofPattern("HH")
|
val timeFormatter = DateTimeFormatter.ofPattern("HH")
|
||||||
|
val time = LocalTime.of(hour.value, 0)
|
||||||
val response = restTemplate.exchange(
|
val response = restTemplate.exchange(
|
||||||
"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/" +
|
"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,
|
HttpMethod.GET,
|
||||||
requestEntity,
|
requestEntity,
|
||||||
BahnTimetable::class.java
|
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
|
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.repository.ScheduledFetchTaskRepository
|
||||||
import de.twomartens.timetable.bahnApi.service.BahnApiService
|
import de.twomartens.timetable.bahnApi.service.BahnApiService
|
||||||
import de.twomartens.timetable.bahnApi.service.BahnDatabaseService
|
import de.twomartens.timetable.bahnApi.service.BahnDatabaseService
|
||||||
import de.twomartens.timetable.bahnApi.tasks.DeleteScheduledTask
|
import de.twomartens.timetable.bahnApi.tasks.DeleteScheduledTask
|
||||||
import de.twomartens.timetable.bahnApi.tasks.FetchTimetableTask
|
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 de.twomartens.timetable.property.TimeProperties
|
||||||
|
import mu.KotlinLogging
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class ScheduledTaskService(
|
class ScheduledTaskService(
|
||||||
@@ -19,8 +25,41 @@ class ScheduledTaskService(
|
|||||||
private val bahnDatabaseService: BahnDatabaseService,
|
private val bahnDatabaseService: BahnDatabaseService,
|
||||||
private val threadPoolTaskExecutor: ThreadPoolTaskExecutor
|
private val threadPoolTaskExecutor: ThreadPoolTaskExecutor
|
||||||
) {
|
) {
|
||||||
|
private val tasks: Tasks = Tasks()
|
||||||
|
|
||||||
fun initializeScheduledTasks() {
|
fun initializeScheduledTasks() {
|
||||||
|
log.info { "Initialize persisted scheduled fetch tasks" }
|
||||||
val scheduledTasks = scheduledFetchTaskRepository.findAll()
|
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 {
|
scheduledTasks.forEach {
|
||||||
val zonedExecutionTime = it.scheduledExecutionDateTime
|
val zonedExecutionTime = it.scheduledExecutionDateTime
|
||||||
.atZone(timeProperties.defaultTimeZone)
|
.atZone(timeProperties.defaultTimeZone)
|
||||||
@@ -31,4 +70,8 @@ class ScheduledTaskService(
|
|||||||
threadPoolTaskScheduler.schedule(deleteTask, zonedExecutionTime.toInstant())
|
threadPoolTaskScheduler.schedule(deleteTask, zonedExecutionTime.toInstant())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = KotlinLogging.logger {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user