mirror of
https://github.com/2martens/tsw-timetable.git
synced 2026-10-17 16:06:51 +02:00
Follow clean code more closely
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
package de.twomartens.timetable
|
||||
|
||||
import de.twomartens.timetable.service.ScheduledTaskService
|
||||
import de.twomartens.timetable.bahnApi.service.ScheduledTaskService
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
package de.twomartens.timetable.configuration
|
||||
package de.twomartens.timetable.bahnApi.configuration
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
@@ -8,6 +8,7 @@ private const val CORE_POOL_SIZE = 1
|
||||
|
||||
@Configuration
|
||||
open class ThreadPoolTaskExecutorConfig {
|
||||
|
||||
@Bean
|
||||
open fun threadPoolTaskExecutor(): ThreadPoolTaskExecutor {
|
||||
val executor = ThreadPoolTaskExecutor()
|
||||
+1
-2
@@ -1,11 +1,10 @@
|
||||
package de.twomartens.timetable.configuration
|
||||
package de.twomartens.timetable.bahnApi.configuration
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
|
||||
|
||||
private const val POOL_SIZE = 1
|
||||
|
||||
private const val THREAD_NAME_PREFIX = "ThreadPoolTaskScheduler"
|
||||
|
||||
@Configuration
|
||||
+3
-2
@@ -3,17 +3,18 @@ package de.twomartens.timetable.bahnApi.jaxb
|
||||
import jakarta.xml.bind.annotation.adapters.XmlAdapter
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.DateTimeParseException
|
||||
import java.util.*
|
||||
|
||||
|
||||
class LocalDateTimeAdapter : XmlAdapter<String?, LocalDateTime>() {
|
||||
|
||||
@Throws(Exception::class)
|
||||
@Throws(DateTimeParseException::class)
|
||||
override fun unmarshal(xmlValue: String?): LocalDateTime {
|
||||
return LocalDateTime.parse(xmlValue, dateFormatter.get())
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
@Throws(DateTimeParseException::class)
|
||||
override fun marshal(kotlinValue: LocalDateTime): String? {
|
||||
return dateFormatter.get().format(kotlinValue)
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
package de.twomartens.timetable.bahnApi.mapper
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.db.BahnTimetable
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
import org.mapstruct.*
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@@ -16,9 +17,9 @@ interface BahnTimetableMapper {
|
||||
fun mapToDB(dto: de.twomartens.timetable.bahnApi.model.dto.BahnTimetable,
|
||||
dateTime: LocalDateTime): BahnTimetable {
|
||||
return BahnTimetable(
|
||||
dto.eva!!,
|
||||
dto.eva,
|
||||
dateTime,
|
||||
dto.station,
|
||||
NonEmptyString(dto.station),
|
||||
dto.stops
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.twomartens.timetable.bahnApi.model
|
||||
|
||||
import de.twomartens.timetable.model.db.StationId
|
||||
|
||||
@JvmInline
|
||||
value class Eva(val value: Int) {
|
||||
init {
|
||||
if (isKnown()) {
|
||||
require(value in 8000000..8999999) {
|
||||
"Eva number must start with an 8 and have 7 digits"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isKnown() = value != -1
|
||||
|
||||
companion object {
|
||||
val UNKNOWN = Eva(-1)
|
||||
|
||||
fun of(stationId: StationId): Eva {
|
||||
return Eva(stationId.stationIdWithinCountry.name.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package de.twomartens.timetable.bahnApi.model
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
data class FetchDates(val previousDay: LocalDate, val fetchDate: LocalDate, val nextDate: LocalDate)
|
||||
@@ -0,0 +1,58 @@
|
||||
package de.twomartens.timetable.bahnApi.model
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.db.ScheduledFetchTask
|
||||
import de.twomartens.timetable.model.base.HourAtDay
|
||||
import org.springframework.stereotype.Component
|
||||
import java.time.LocalDateTime
|
||||
|
||||
private const val SECONDS_BETWEEN_TASKS = 2
|
||||
|
||||
private const val ZERO = 0
|
||||
|
||||
@Component
|
||||
class TaskFactory {
|
||||
|
||||
private val _tasksPerHour: MutableMap<HourAtDay, Int> = mutableMapOf()
|
||||
|
||||
fun initializeWithExistingTasks(existingTasks: List<ScheduledFetchTask>) {
|
||||
existingTasks.forEach { countTask(it) }
|
||||
}
|
||||
|
||||
fun createTaskAndUpdateCounter(eva: Eva, hourAtDay: HourAtDay): ScheduledFetchTask {
|
||||
val fetchDateTime = calculateNextAvailableFetchDateTime(hourAtDay)
|
||||
incrementTaskCountForHourAtDay(hourAtDay)
|
||||
return ScheduledFetchTask(eva, hourAtDay, fetchDateTime)
|
||||
}
|
||||
|
||||
private fun calculateNextAvailableFetchDateTime(hourAtDay: HourAtDay): LocalDateTime {
|
||||
val secondInHourToExecute = calculateSecondInHourToCalculate(hourAtDay)
|
||||
return calculateFetchDateTime(hourAtDay, secondInHourToExecute)
|
||||
}
|
||||
|
||||
private fun calculateSecondInHourToCalculate(hourAtDay: HourAtDay): Int {
|
||||
val numberOfTasksInHour = getNumberOfTasksInHour(hourAtDay)
|
||||
return numberOfTasksInHour * SECONDS_BETWEEN_TASKS
|
||||
}
|
||||
|
||||
private fun calculateFetchDateTime(
|
||||
hourAtDay: HourAtDay,
|
||||
secondInHourToExecute: Int
|
||||
): LocalDateTime {
|
||||
return hourAtDay.dateTime.plusSeconds(secondInHourToExecute.toLong())
|
||||
}
|
||||
|
||||
private fun countTask(scheduledFetchTask: ScheduledFetchTask) {
|
||||
val scheduledTime = scheduledFetchTask.scheduledExecutionDateTime
|
||||
val hourAtDay = HourAtDay.of(scheduledTime)
|
||||
incrementTaskCountForHourAtDay(hourAtDay)
|
||||
}
|
||||
|
||||
private fun incrementTaskCountForHourAtDay(hourAtDay: HourAtDay) {
|
||||
val numberOfTasksInHour = getNumberOfTasksInHour(hourAtDay)
|
||||
_tasksPerHour[hourAtDay] = numberOfTasksInHour + 1
|
||||
}
|
||||
|
||||
private fun getNumberOfTasksInHour(hourAtDay: HourAtDay) =
|
||||
_tasksPerHour.computeIfAbsent(hourAtDay) { ZERO }
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package de.twomartens.timetable.bahnApi.model.db
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.model.NonEmptyString
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.annotation.CreatedDate
|
||||
import org.springframework.data.annotation.Id
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package de.twomartens.timetable.bahnApi.model.db
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
import de.twomartens.timetable.bahnApi.model.dto.BahnStationStop
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.model.NonEmptyString
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.annotation.CreatedDate
|
||||
import org.springframework.data.annotation.Id
|
||||
|
||||
+4
-6
@@ -1,7 +1,7 @@
|
||||
package de.twomartens.timetable.bahnApi.model.db
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.model.Hour
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
import de.twomartens.timetable.model.base.HourAtDay
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.annotation.CreatedDate
|
||||
import org.springframework.data.annotation.Id
|
||||
@@ -9,15 +9,13 @@ import org.springframework.data.annotation.LastModifiedDate
|
||||
import org.springframework.data.mongodb.core.index.CompoundIndex
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Document
|
||||
@CompoundIndex(def = "{'eva': 1, 'fetchedDate': 1, 'fetchedTime': 1", unique = true)
|
||||
@CompoundIndex(def = "{'eva': 1, 'fetchedDateTime': 1", unique = true)
|
||||
data class ScheduledFetchTask(
|
||||
var eva: Eva,
|
||||
var fetchedDate: LocalDate,
|
||||
var fetchedTime: Hour,
|
||||
var fetchedDateTime: HourAtDay,
|
||||
var scheduledExecutionDateTime: LocalDateTime
|
||||
) {
|
||||
@Id
|
||||
|
||||
+6
-10
@@ -1,20 +1,16 @@
|
||||
package de.twomartens.timetable.bahnApi.model.dto
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
import jakarta.xml.bind.annotation.XmlAccessType
|
||||
import jakarta.xml.bind.annotation.XmlAccessorType
|
||||
import jakarta.xml.bind.annotation.XmlAttribute
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
data class BahnStation(
|
||||
@field:XmlAttribute
|
||||
var name: String,
|
||||
@field:XmlAttribute
|
||||
var eva: Eva?,
|
||||
@field:XmlAttribute
|
||||
var ds100: String,
|
||||
@field:XmlAttribute
|
||||
var db: Boolean
|
||||
@field:XmlAttribute var name: String,
|
||||
@field:XmlAttribute var eva: Eva,
|
||||
@field:XmlAttribute var ds100: String,
|
||||
@field:XmlAttribute var db: Boolean
|
||||
) {
|
||||
constructor(): this("", null, "", true)
|
||||
constructor() : this("", Eva.UNKNOWN, "", true)
|
||||
}
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
package de.twomartens.timetable.bahnApi.model.dto
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
import jakarta.xml.bind.annotation.XmlAccessType
|
||||
import jakarta.xml.bind.annotation.XmlAccessorType
|
||||
import jakarta.xml.bind.annotation.XmlAttribute
|
||||
@@ -8,11 +8,11 @@ import jakarta.xml.bind.annotation.XmlElement
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
data class BahnStationStop(
|
||||
@field:XmlAttribute var eva: Eva?,
|
||||
@field:XmlAttribute var id: String,
|
||||
@field:XmlElement(name = "tl") var tripLabel: BahnTripLabel?,
|
||||
@field:XmlElement(name = "ar") var arrival: BahnStopEvent?,
|
||||
@field:XmlElement(name = "dp") var departure: BahnStopEvent?
|
||||
@field:XmlAttribute var eva: Eva,
|
||||
@field:XmlAttribute var id: String,
|
||||
@field:XmlElement(name = "tl") var tripLabel: BahnTripLabel,
|
||||
@field:XmlElement(name = "ar") var arrival: BahnStopEvent?,
|
||||
@field:XmlElement(name = "dp") var departure: BahnStopEvent?
|
||||
) {
|
||||
constructor(): this(null, "", null, null, null)
|
||||
constructor() : this(Eva.UNKNOWN, "", BahnTripLabel(), null, null)
|
||||
}
|
||||
+1
-1
@@ -10,5 +10,5 @@ import jakarta.xml.bind.annotation.XmlRootElement
|
||||
data class BahnStations(
|
||||
@field:XmlElement(name = "station") var stations: List<BahnStation>
|
||||
) {
|
||||
constructor(): this(mutableListOf())
|
||||
constructor() : this(listOf())
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,14 +1,14 @@
|
||||
package de.twomartens.timetable.bahnApi.model.dto
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
import jakarta.xml.bind.annotation.*
|
||||
|
||||
@XmlRootElement(name = "timetable")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
data class BahnTimetable(
|
||||
@field:XmlAttribute var eva: Eva?,
|
||||
@field:XmlAttribute var station: String,
|
||||
@field:XmlElement(name = "s") var stops: List<BahnStationStop>
|
||||
@field:XmlAttribute var eva: Eva,
|
||||
@field:XmlAttribute var station: String,
|
||||
@field:XmlElement(name = "s") var stops: List<BahnStationStop>
|
||||
) {
|
||||
constructor(): this(null, "", mutableListOf())
|
||||
constructor() : this(Eva.UNKNOWN, "", listOf())
|
||||
}
|
||||
+2
-3
@@ -11,8 +11,7 @@ data class BahnTripLabel(
|
||||
@field:XmlAttribute(name = "f") var filterFlags: String,
|
||||
@field:XmlAttribute(name = "n") var trainNumber: String,
|
||||
@field:XmlAttribute(name = "o") var owner: String,
|
||||
@field:XmlAttribute(name = "t") var tripType: BahnTripType?
|
||||
@field:XmlAttribute(name = "t") var tripType: BahnTripType
|
||||
) {
|
||||
constructor(): this("", "", "", "",
|
||||
null)
|
||||
constructor() : this("", "", "", "", BahnTripType.p)
|
||||
}
|
||||
+5
-6
@@ -1,18 +1,17 @@
|
||||
package de.twomartens.timetable.bahnApi.service
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
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 de.twomartens.timetable.model.base.HourAtDay
|
||||
import org.springframework.http.HttpEntity
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@@ -33,14 +32,14 @@ class BahnApiService(
|
||||
return body?.stations ?: listOf()
|
||||
}
|
||||
|
||||
fun fetchTimetable(eva: Eva, date: LocalDate, hour: Hour): BahnTimetable {
|
||||
fun fetchTimetable(eva: Eva, hourAtDay: HourAtDay): BahnTimetable {
|
||||
val requestEntity = buildRequestEntity<BahnTimetable>()
|
||||
val dateFormatter = DateTimeFormatter.ofPattern("yyMMdd")
|
||||
val timeFormatter = DateTimeFormatter.ofPattern("HH")
|
||||
val time = LocalTime.of(hour.value, 0)
|
||||
val time = LocalTime.of(hourAtDay.hour.value, 0)
|
||||
val response = restTemplate.exchange(
|
||||
"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/" +
|
||||
"${eva}/${date.format(dateFormatter)}/${time.format(timeFormatter)}",
|
||||
"${eva}/${hourAtDay.date.format(dateFormatter)}/${time.format(timeFormatter)}",
|
||||
HttpMethod.GET,
|
||||
requestEntity,
|
||||
BahnTimetable::class.java
|
||||
|
||||
+3
-3
@@ -6,9 +6,9 @@ import de.twomartens.timetable.bahnApi.model.dto.BahnStation
|
||||
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
|
||||
import de.twomartens.timetable.bahnApi.repository.BahnStationRepository
|
||||
import de.twomartens.timetable.bahnApi.repository.BahnTimetableRepository
|
||||
import de.twomartens.timetable.model.base.HourAtDay
|
||||
import org.mapstruct.factory.Mappers
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Service
|
||||
class BahnDatabaseService(
|
||||
@@ -23,7 +23,7 @@ class BahnDatabaseService(
|
||||
.map { bahnStationMapper.mapToDB(it) })
|
||||
}
|
||||
|
||||
fun storeTimetable(timetable: BahnTimetable, dateTime: LocalDateTime) {
|
||||
bahnTimetableRepository.save(bahnTimetableMapper.mapToDB(timetable, dateTime))
|
||||
fun storeTimetable(timetable: BahnTimetable, hourAtDay: HourAtDay) {
|
||||
bahnTimetableRepository.save(bahnTimetableMapper.mapToDB(timetable, hourAtDay.dateTime))
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package de.twomartens.timetable.bahnApi.service
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
import de.twomartens.timetable.bahnApi.model.FetchDates
|
||||
import de.twomartens.timetable.bahnApi.model.TaskFactory
|
||||
import de.twomartens.timetable.bahnApi.model.db.ScheduledFetchTask
|
||||
import de.twomartens.timetable.bahnApi.repository.ScheduledFetchTaskRepository
|
||||
import de.twomartens.timetable.model.base.Hour
|
||||
import de.twomartens.timetable.model.base.HourAtDay
|
||||
import de.twomartens.timetable.model.db.TswRoute
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.LocalDate
|
||||
|
||||
@Service
|
||||
class ScheduledTaskService(
|
||||
private val scheduledFetchTaskRepository: ScheduledFetchTaskRepository,
|
||||
private val taskFactory: TaskFactory,
|
||||
private val taskScheduler: TaskScheduler
|
||||
) {
|
||||
fun initializeScheduledTasks() {
|
||||
log.info { "Initialize persisted scheduled fetch tasks" }
|
||||
val scheduledTasks = scheduledFetchTaskRepository.findAll()
|
||||
taskFactory.initializeWithExistingTasks(scheduledTasks)
|
||||
taskScheduler.scheduleFetchTasks(scheduledTasks)
|
||||
}
|
||||
|
||||
fun scheduleTimetableFetch(tswRoute: TswRoute, fetchedDate: LocalDate) {
|
||||
log.info {
|
||||
"Schedule timetable fetch: [route ${tswRoute.name}]"
|
||||
}
|
||||
val fetchDates = calculateDatesToFetch(fetchedDate)
|
||||
val newTasks = buildScheduledTasks(tswRoute, fetchDates)
|
||||
|
||||
storeTasksInDatabase(newTasks)
|
||||
taskScheduler.scheduleFetchTasks(newTasks)
|
||||
}
|
||||
|
||||
private fun calculateDatesToFetch(fetchedDate: LocalDate): FetchDates {
|
||||
val previousDay = fetchedDate.minusDays(1)
|
||||
val nextDay = fetchedDate.plusDays(1)
|
||||
return FetchDates(previousDay, fetchedDate, nextDay)
|
||||
}
|
||||
|
||||
private fun buildScheduledTasks(
|
||||
tswRoute: TswRoute,
|
||||
fetchDates: FetchDates
|
||||
): List<ScheduledFetchTask> {
|
||||
val newTasks = mutableListOf<ScheduledFetchTask>()
|
||||
tswRoute.stationIds.forEach {
|
||||
val stationId = it
|
||||
val eva = Eva.of(stationId)
|
||||
var hourAtDay = HourAtDay.of(Hour.of(23), fetchDates.previousDay)
|
||||
var newTask = taskFactory.createTaskAndUpdateCounter(eva, hourAtDay)
|
||||
newTasks.add(newTask)
|
||||
for (hour in 0..23) {
|
||||
hourAtDay = HourAtDay.of(Hour.of(hour), fetchDates.fetchDate)
|
||||
newTask = taskFactory.createTaskAndUpdateCounter(eva, hourAtDay)
|
||||
newTasks.add(newTask)
|
||||
}
|
||||
for (hour in 0..3) {
|
||||
hourAtDay = HourAtDay.of(Hour.of(hour), fetchDates.nextDate)
|
||||
newTask = taskFactory.createTaskAndUpdateCounter(eva, hourAtDay)
|
||||
newTasks.add(newTask)
|
||||
}
|
||||
}
|
||||
return newTasks
|
||||
}
|
||||
|
||||
private fun storeTasksInDatabase(newTasks: List<ScheduledFetchTask>) {
|
||||
scheduledFetchTaskRepository.saveAll(newTasks)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = KotlinLogging.logger {}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package de.twomartens.timetable.bahnApi.service
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.db.ScheduledFetchTask
|
||||
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
|
||||
import de.twomartens.timetable.bahnApi.repository.ScheduledFetchTaskRepository
|
||||
import de.twomartens.timetable.bahnApi.tasks.DeleteScheduledTask
|
||||
import de.twomartens.timetable.bahnApi.tasks.FetchTimetableTask
|
||||
import de.twomartens.timetable.bahnApi.tasks.StoreTimetableTask
|
||||
import de.twomartens.timetable.model.base.HourAtDay
|
||||
import de.twomartens.timetable.property.TimeProperties
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
@Service
|
||||
class TaskScheduler(
|
||||
private val timeProperties: TimeProperties,
|
||||
private val threadPoolTaskScheduler: ThreadPoolTaskScheduler,
|
||||
private val threadPoolTaskExecutor: ThreadPoolTaskExecutor,
|
||||
private val bahnApiService: BahnApiService,
|
||||
private val bahnDatabaseService: BahnDatabaseService,
|
||||
private val scheduledFetchTaskRepository: ScheduledFetchTaskRepository
|
||||
) {
|
||||
fun scheduleFetchTasks(scheduledTasks: List<ScheduledFetchTask>) {
|
||||
scheduledTasks.forEach {
|
||||
val zonedExecutionTime = it.scheduledExecutionDateTime
|
||||
.atZone(timeProperties.defaultTimeZone)
|
||||
scheduleDeleteTask(it, zonedExecutionTime)
|
||||
scheduleFetchTimetableTask(it, zonedExecutionTime)
|
||||
}
|
||||
}
|
||||
|
||||
fun scheduleStoreTask(timetable: BahnTimetable, hourAtDay: HourAtDay) {
|
||||
val storeTask = StoreTimetableTask(timetable, hourAtDay, bahnDatabaseService)
|
||||
threadPoolTaskExecutor.execute(storeTask)
|
||||
}
|
||||
|
||||
private fun scheduleDeleteTask(
|
||||
scheduledFetchTask: ScheduledFetchTask,
|
||||
zonedExecutionTime: ZonedDateTime
|
||||
) {
|
||||
val deleteTask = DeleteScheduledTask(scheduledFetchTaskRepository, scheduledFetchTask)
|
||||
threadPoolTaskScheduler.schedule(deleteTask, zonedExecutionTime.toInstant())
|
||||
}
|
||||
|
||||
private fun scheduleFetchTimetableTask(
|
||||
scheduledFetchTask: ScheduledFetchTask,
|
||||
zonedExecutionTime: ZonedDateTime
|
||||
) {
|
||||
val timetableTask = FetchTimetableTask(scheduledFetchTask.eva,
|
||||
scheduledFetchTask.fetchedDateTime,
|
||||
bahnApiService, this)
|
||||
threadPoolTaskScheduler.schedule(timetableTask, zonedExecutionTime.toInstant())
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -14,8 +14,8 @@ open class DeleteScheduledTask(
|
||||
log.info {
|
||||
"Delete scheduled fetch task after the fetch occurred: " +
|
||||
"[eva: ${scheduledFetchTask.eva}], " +
|
||||
"[date: ${scheduledFetchTask.fetchedDate}], " +
|
||||
"[time: ${scheduledFetchTask.fetchedTime}]"
|
||||
"[date: ${scheduledFetchTask.fetchedDateTime.date}], " +
|
||||
"[time: ${scheduledFetchTask.fetchedDateTime.hour}]"
|
||||
}
|
||||
scheduledFetchTaskRepository.delete(scheduledFetchTask)
|
||||
}
|
||||
|
||||
+8
-14
@@ -1,30 +1,24 @@
|
||||
package de.twomartens.timetable.bahnApi.tasks
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.Eva
|
||||
import de.twomartens.timetable.bahnApi.service.BahnApiService
|
||||
import de.twomartens.timetable.bahnApi.service.BahnDatabaseService
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.model.Hour
|
||||
import de.twomartens.timetable.bahnApi.service.TaskScheduler
|
||||
import de.twomartens.timetable.model.base.HourAtDay
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.scheduling.annotation.Async
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
|
||||
import java.time.LocalDate
|
||||
|
||||
@Async
|
||||
open class FetchTimetableTask(
|
||||
private val eva: Eva,
|
||||
private val date: LocalDate,
|
||||
private val hour: Hour,
|
||||
private val hourAtDay: HourAtDay,
|
||||
private val bahnApiService: BahnApiService,
|
||||
private val bahnDatabaseService: BahnDatabaseService,
|
||||
private val executor: ThreadPoolTaskExecutor) : Runnable {
|
||||
private val scheduler: TaskScheduler) : Runnable {
|
||||
override fun run() {
|
||||
log.info {
|
||||
"Fetch timetable: [eva: $eva], [date: $date], [hour: $hour]"
|
||||
"Fetch timetable: [eva: $eva], [date: ${hourAtDay.date}], [hour: ${hourAtDay.hour}]"
|
||||
}
|
||||
val timetable = bahnApiService.fetchTimetable(eva, date, hour)
|
||||
val storeTask = StoreTimetableTask(timetable, date.atTime(hour.value, 0),
|
||||
bahnDatabaseService)
|
||||
executor.execute(storeTask)
|
||||
val timetable = bahnApiService.fetchTimetable(eva, hourAtDay)
|
||||
scheduler.scheduleStoreTask(timetable, hourAtDay)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+3
-3
@@ -2,19 +2,19 @@ package de.twomartens.timetable.bahnApi.tasks
|
||||
|
||||
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
|
||||
import de.twomartens.timetable.bahnApi.service.BahnDatabaseService
|
||||
import de.twomartens.timetable.model.base.HourAtDay
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.scheduling.annotation.Async
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Async
|
||||
open class StoreTimetableTask(
|
||||
private val timetable: BahnTimetable,
|
||||
private val dateTime: LocalDateTime,
|
||||
private val hourAtDay: HourAtDay,
|
||||
private val bahnDatabaseService: BahnDatabaseService
|
||||
) : Runnable {
|
||||
override fun run() {
|
||||
log.info { "Store timetable: [eva: ${timetable.eva}], station: ${timetable.station}]" }
|
||||
bahnDatabaseService.storeTimetable(timetable, dateTime)
|
||||
bahnDatabaseService.storeTimetable(timetable, hourAtDay)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+8
-4
@@ -1,8 +1,9 @@
|
||||
package de.twomartens.timetable.controller
|
||||
|
||||
import de.twomartens.timetable.model.NonEmptyString
|
||||
import de.twomartens.timetable.bahnApi.service.ScheduledTaskService
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
import de.twomartens.timetable.model.db.UserId
|
||||
import de.twomartens.timetable.repository.TswRouteRepository
|
||||
import de.twomartens.timetable.service.ScheduledTaskService
|
||||
import io.swagger.v3.oas.annotations.Operation
|
||||
import io.swagger.v3.oas.annotations.Parameter
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||
@@ -34,8 +35,11 @@ class TimetableController(
|
||||
description = "Request does not follow specification"
|
||||
)]
|
||||
)
|
||||
@PostMapping("/{routeName}/fetchTimetable/{date}")
|
||||
@PostMapping("/{userId}/{routeName}/fetchTimetable/{date}")
|
||||
fun scheduleFetch(
|
||||
@PathVariable @Parameter(description = "The id of the user",
|
||||
example = "1",
|
||||
required = true) userId: UserId,
|
||||
@PathVariable @Parameter(description = "The name of the TSW route.",
|
||||
example = "CologneAachen",
|
||||
required = true) routeName: NonEmptyString,
|
||||
@@ -47,7 +51,7 @@ class TimetableController(
|
||||
if (!date.isAfter(LocalDate.now(clock))) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Date must be in the future")
|
||||
}
|
||||
val route = routeRepository.findByName(routeName)
|
||||
val route = routeRepository.findByUserIdAndName(userId, routeName)
|
||||
?: throw ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||
"Route name must belong to existing route")
|
||||
scheduledTaskService.scheduleTimetableFetch(route, date)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
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,16 @@
|
||||
package de.twomartens.timetable.model.base
|
||||
|
||||
@JvmInline
|
||||
value class Hour private constructor(val value: Int) {
|
||||
init {
|
||||
require(value in 0..23) {
|
||||
"Hour must be between 0 and 23"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(hour: Int): Hour {
|
||||
return Hour(hour)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package de.twomartens.timetable.model.base
|
||||
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
@JvmInline
|
||||
value class HourAtDay private constructor(private val value: LocalDateTime) {
|
||||
init {
|
||||
require(value.minute == 0 && value.second == 0 && value.nano == 0) {
|
||||
"DateTime must only include an hour and the date."
|
||||
}
|
||||
}
|
||||
|
||||
val date: LocalDate
|
||||
get() {
|
||||
return value.toLocalDate()
|
||||
}
|
||||
|
||||
val hour: Hour
|
||||
get() {
|
||||
return Hour.of(value.hour)
|
||||
}
|
||||
|
||||
val dateTime: LocalDateTime
|
||||
get() {
|
||||
return value
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(dateTime: LocalDateTime): HourAtDay {
|
||||
return HourAtDay(dateTime.truncatedTo(ChronoUnit.HOURS))
|
||||
}
|
||||
|
||||
fun of(hour: Hour, date: LocalDate): HourAtDay {
|
||||
return HourAtDay(date.atTime(hour.value, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package de.twomartens.timetable.model
|
||||
package de.twomartens.timetable.model.base
|
||||
|
||||
@JvmInline
|
||||
value class NonEmptyString(val name: String) {
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.twomartens.timetable.model.base
|
||||
|
||||
@JvmInline
|
||||
value class PositiveInteger(val value: Int) {
|
||||
init {
|
||||
require(value > 0) {
|
||||
"Value must be positive integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.PositiveInteger
|
||||
|
||||
@JvmInline
|
||||
value class CoachCapacity(val capacity: PositiveInteger)
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
|
||||
@JvmInline
|
||||
value class CountryCode(val countryCode: NonEmptyString) {
|
||||
init {
|
||||
require(countryCode.name.length == 2) {
|
||||
"CountryCode must be only two characters long to conform to ISO 3166-1 alpha-2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
import kotlin.time.Duration
|
||||
|
||||
data class Depot(
|
||||
var name: NonEmptyString,
|
||||
var nearestStationId: StationId,
|
||||
var tracks: Map<TrackId, CoachCapacity>,
|
||||
var formationTimes: Map<FormationId, Duration>
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
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.CompoundIndex
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
import java.time.Instant
|
||||
|
||||
@Document
|
||||
@CompoundIndex(def = "{'userId': 1, 'formationId': 1}", unique = true)
|
||||
data class Formation(
|
||||
var userId: UserId,
|
||||
var positiveInteger: FormationId,
|
||||
var name: NonEmptyString
|
||||
) {
|
||||
@Id
|
||||
var id: ObjectId = ObjectId()
|
||||
|
||||
@CreatedDate
|
||||
lateinit var created: Instant
|
||||
|
||||
@LastModifiedDate
|
||||
lateinit var lastModified: Instant
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.PositiveInteger
|
||||
|
||||
@JvmInline
|
||||
value class FormationId(val id: PositiveInteger)
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
import kotlin.time.Duration
|
||||
|
||||
data class Portal(
|
||||
var name: NonEmptyString,
|
||||
var nearestStationId: StationId,
|
||||
var formationTimes: Map<FormationId, Duration>
|
||||
)
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
data class Section(
|
||||
var name: String
|
||||
)
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
|
||||
@JvmInline
|
||||
value class Section(val name: NonEmptyString)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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
|
||||
@@ -11,7 +10,7 @@ import java.time.Instant
|
||||
|
||||
@Document
|
||||
data class Station(
|
||||
@Indexed(unique = true) var eva: Eva,
|
||||
@Indexed(unique = true) var stationId: StationId,
|
||||
var name: String,
|
||||
var platforms: List<Platform>
|
||||
) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
|
||||
@JvmInline
|
||||
value class StationId(val id: NonEmptyString) {
|
||||
init {
|
||||
require(idPattern.matches(id.name)) {
|
||||
"StationId must start with two character country code, followed by a dash, " +
|
||||
"and at least one word character"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val idPattern = Regex("^\\w{2}-(?<countryStationId>\\w.*)")
|
||||
}
|
||||
|
||||
val stationIdWithinCountry: NonEmptyString
|
||||
get() {
|
||||
val matchResult = idPattern.matchEntire(id.name)
|
||||
val groupMatch = matchResult?.groups?.get("countryStationId")?.value
|
||||
return when {
|
||||
matchResult == null || groupMatch == null ->
|
||||
throw IllegalArgumentException("id wasn't matched by pattern")
|
||||
|
||||
else -> NonEmptyString(groupMatch)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.PositiveInteger
|
||||
|
||||
@JvmInline
|
||||
value class TrackId(val id: PositiveInteger)
|
||||
@@ -1,30 +1,23 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.Eva
|
||||
import de.twomartens.timetable.model.NonEmptyString
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
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.index.CompoundIndex
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
import java.time.Instant
|
||||
|
||||
@Document
|
||||
@CompoundIndex(def = "{'userId': 1, 'name': 1}", unique = true)
|
||||
data class TswRoute(
|
||||
@Indexed(unique = true) var name: NonEmptyString,
|
||||
/**
|
||||
* 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>
|
||||
var userId: UserId,
|
||||
var name: NonEmptyString,
|
||||
var countryCode: CountryCode,
|
||||
var stationIds: List<StationId>,
|
||||
var portals: List<Portal>,
|
||||
var depots: List<Depot>
|
||||
) {
|
||||
@Id
|
||||
var id: ObjectId = ObjectId()
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
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.mapping.Document
|
||||
import java.time.Instant
|
||||
|
||||
@Document
|
||||
data class User(
|
||||
var userId: UserId,
|
||||
var name: NonEmptyString
|
||||
) {
|
||||
@Id
|
||||
var id: ObjectId = ObjectId()
|
||||
|
||||
@CreatedDate
|
||||
lateinit var created: Instant
|
||||
|
||||
@LastModifiedDate
|
||||
lateinit var lastModified: Instant
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.twomartens.timetable.model.db
|
||||
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
|
||||
@JvmInline
|
||||
value class UserId(val id: NonEmptyString)
|
||||
+3
-2
@@ -1,10 +1,11 @@
|
||||
package de.twomartens.timetable.repository
|
||||
|
||||
import de.twomartens.timetable.model.NonEmptyString
|
||||
import de.twomartens.timetable.model.base.NonEmptyString
|
||||
import de.twomartens.timetable.model.db.TswRoute
|
||||
import de.twomartens.timetable.model.db.UserId
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.mongodb.repository.MongoRepository
|
||||
|
||||
interface TswRouteRepository : MongoRepository<TswRoute, ObjectId> {
|
||||
fun findByName(name: NonEmptyString): TswRoute?
|
||||
fun findByUserIdAndName(userId: UserId, name: NonEmptyString): TswRoute?
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
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(
|
||||
private val timeProperties: TimeProperties,
|
||||
private val scheduledFetchTaskRepository: ScheduledFetchTaskRepository,
|
||||
private val threadPoolTaskScheduler: ThreadPoolTaskScheduler,
|
||||
private val bahnApiService: BahnApiService,
|
||||
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)
|
||||
val deleteTask = DeleteScheduledTask(scheduledFetchTaskRepository, it)
|
||||
val timetableTask = FetchTimetableTask(it.eva, it.fetchedDate, it.fetchedTime,
|
||||
bahnApiService, bahnDatabaseService, threadPoolTaskExecutor)
|
||||
threadPoolTaskScheduler.schedule(timetableTask, zonedExecutionTime.toInstant())
|
||||
threadPoolTaskScheduler.schedule(deleteTask, zonedExecutionTime.toInstant())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = KotlinLogging.logger {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user