mirror of
https://github.com/2martens/tsw-timetable.git
synced 2026-10-17 16:06:51 +02:00
Implemented BahnAPI to fetch stations and timetables
This commit is contained in:
@@ -22,7 +22,7 @@ open class MainApplication(
|
|||||||
fun ready() {
|
fun ready() {
|
||||||
val result = bahnApiService.fetchStations("Köln Hbf")
|
val result = bahnApiService.fetchStations("Köln Hbf")
|
||||||
val koeln = result[0]
|
val koeln = result[0]
|
||||||
val stops = bahnApiService.fetchTimetable(koeln.eva,
|
val timetable = bahnApiService.fetchTimetable(koeln.eva,
|
||||||
LocalDate.of(2023, Month.SEPTEMBER, 23),
|
LocalDate.of(2023, Month.SEPTEMBER, 23),
|
||||||
LocalTime.of(17, 0))
|
LocalTime.of(17, 0))
|
||||||
log.info("stations: $result")
|
log.info("stations: $result")
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.jaxb
|
||||||
|
|
||||||
|
import jakarta.xml.bind.annotation.adapters.XmlAdapter
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class LocalDateTimeAdapter : XmlAdapter<String?, LocalDateTime>() {
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun unmarshal(xmlValue: String?): LocalDateTime {
|
||||||
|
return LocalDateTime.parse(xmlValue, dateFormatter.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun marshal(kotlinValue: LocalDateTime): String? {
|
||||||
|
return dateFormatter.get().format(kotlinValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val dateFormatter: ThreadLocal<DateTimeFormatter> = object : ThreadLocal<DateTimeFormatter>() {
|
||||||
|
override fun initialValue(): DateTimeFormatter {
|
||||||
|
return DateTimeFormatter.ofPattern("yyMMddHHmm", Locale.GERMANY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.mapper
|
||||||
|
|
||||||
|
import de.twomartens.timetable.bahnApi.model.db.BahnStation
|
||||||
|
import org.mapstruct.*
|
||||||
|
|
||||||
|
@Mapper(
|
||||||
|
collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
|
||||||
|
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
|
||||||
|
unmappedTargetPolicy = ReportingPolicy.IGNORE
|
||||||
|
)
|
||||||
|
interface BahnStationMapper {
|
||||||
|
|
||||||
|
@Mapping(target = "id", ignore = true)
|
||||||
|
@Mapping(target = "created", ignore = true)
|
||||||
|
@Mapping(target = "lastModified", ignore = true)
|
||||||
|
fun mapToDB(dto: de.twomartens.timetable.bahnApi.model.dto.BahnStation): BahnStation
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.model.db
|
||||||
|
|
||||||
|
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 BahnStation(
|
||||||
|
var name: String,
|
||||||
|
@Indexed(unique = true) var eva: String,
|
||||||
|
var ds100: String,
|
||||||
|
var db: Boolean
|
||||||
|
) {
|
||||||
|
@Id
|
||||||
|
var id: ObjectId = ObjectId()
|
||||||
|
|
||||||
|
@CreatedDate
|
||||||
|
lateinit var created: Instant
|
||||||
|
|
||||||
|
@LastModifiedDate
|
||||||
|
lateinit var lastModified: Instant
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.model.dto
|
||||||
|
|
||||||
|
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: String,
|
||||||
|
@field:XmlAttribute
|
||||||
|
var ds100: String,
|
||||||
|
@field:XmlAttribute
|
||||||
|
var db: Boolean
|
||||||
|
) {
|
||||||
|
constructor(): this("", "", "", true)
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.model.dto
|
||||||
|
|
||||||
|
import jakarta.xml.bind.annotation.XmlAccessType
|
||||||
|
import jakarta.xml.bind.annotation.XmlAccessorType
|
||||||
|
import jakarta.xml.bind.annotation.XmlAttribute
|
||||||
|
import jakarta.xml.bind.annotation.XmlElement
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
data class BahnStationStop(
|
||||||
|
@field:XmlAttribute var eva: Int?,
|
||||||
|
@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)
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.model.dto
|
||||||
|
|
||||||
|
import jakarta.xml.bind.annotation.XmlAccessType
|
||||||
|
import jakarta.xml.bind.annotation.XmlAccessorType
|
||||||
|
import jakarta.xml.bind.annotation.XmlElement
|
||||||
|
import jakarta.xml.bind.annotation.XmlRootElement
|
||||||
|
|
||||||
|
@XmlRootElement(name = "stations")
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
data class BahnStations(
|
||||||
|
@field:XmlElement(name = "station") var stations: List<BahnStation>
|
||||||
|
) {
|
||||||
|
constructor(): this(mutableListOf())
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.model.dto
|
||||||
|
|
||||||
|
import de.twomartens.timetable.bahnApi.jaxb.LocalDateTimeAdapter
|
||||||
|
import jakarta.xml.bind.annotation.XmlAccessType
|
||||||
|
import jakarta.xml.bind.annotation.XmlAccessorType
|
||||||
|
import jakarta.xml.bind.annotation.XmlAttribute
|
||||||
|
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
data class BahnStopEvent(
|
||||||
|
@field:XmlAttribute(name = "pde") var plannedDistantEndpoint: String,
|
||||||
|
@field:XmlAttribute(name = "cde") var changedDistantEndpoint: String,
|
||||||
|
@field:XmlAttribute(name = "pt")
|
||||||
|
@field:XmlJavaTypeAdapter(value = LocalDateTimeAdapter::class)
|
||||||
|
var plannedTime: LocalDateTime?,
|
||||||
|
@field:XmlAttribute(name = "ct")
|
||||||
|
@field:XmlJavaTypeAdapter(value = LocalDateTimeAdapter::class)
|
||||||
|
var changedTime: LocalDateTime?,
|
||||||
|
@field:XmlAttribute(name = "ppth") var plannedPath: String,
|
||||||
|
@field:XmlAttribute(name = "cpth") var changedPath: String,
|
||||||
|
@field:XmlAttribute(name = "pp") var plannedPlatform: String,
|
||||||
|
@field:XmlAttribute(name = "cp") var changedPlatform: String,
|
||||||
|
@field:XmlAttribute(name = "wings") var wings: String,
|
||||||
|
@field:XmlAttribute(name = "l") var line: String
|
||||||
|
) {
|
||||||
|
constructor(): this("", "",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"", "", "", "",
|
||||||
|
"", "")
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.model.dto
|
||||||
|
|
||||||
|
import jakarta.xml.bind.annotation.*
|
||||||
|
|
||||||
|
@XmlRootElement(name = "timetable")
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
data class BahnTimetable(
|
||||||
|
@field:XmlAttribute var eva: Int?,
|
||||||
|
@field:XmlAttribute var station: String,
|
||||||
|
@field:XmlElement(name = "s") var stops: List<BahnStationStop>
|
||||||
|
) {
|
||||||
|
constructor(): this(null, "", mutableListOf())
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.model.dto
|
||||||
|
|
||||||
|
import jakarta.xml.bind.annotation.XmlAccessType
|
||||||
|
import jakarta.xml.bind.annotation.XmlAccessorType
|
||||||
|
import jakarta.xml.bind.annotation.XmlAttribute
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
data class BahnTripLabel(
|
||||||
|
@field:XmlAttribute(name = "c") var category: String,
|
||||||
|
// S: S-Bahn, N: Nahverkehr, F: Fernverkehr
|
||||||
|
@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?
|
||||||
|
) {
|
||||||
|
constructor(): this("", "", "", "",
|
||||||
|
null)
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.model.dto
|
||||||
|
|
||||||
|
enum class BahnTripType {
|
||||||
|
p, // passenger?
|
||||||
|
e,
|
||||||
|
z,
|
||||||
|
s,
|
||||||
|
h,
|
||||||
|
n
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.property
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.boot.context.properties.bind.ConstructorBinding
|
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope
|
||||||
|
|
||||||
|
@RefreshScope
|
||||||
|
@ConfigurationProperties(prefix = "de.twomartens.timetable.bahn-api")
|
||||||
|
@Schema(description = "Properties to configure the BahnAPI")
|
||||||
|
data class BahnApiProperties @ConstructorBinding constructor(
|
||||||
|
val clientId: String,
|
||||||
|
val clientSecret: String
|
||||||
|
)
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.repository
|
||||||
|
|
||||||
|
import de.twomartens.timetable.bahnApi.model.db.BahnStation
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository
|
||||||
|
|
||||||
|
interface BahnStationRepository : MongoRepository<BahnStation, String>
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
package de.twomartens.timetable.bahnApi.service
|
||||||
|
|
||||||
|
import de.twomartens.timetable.bahnApi.model.dto.BahnStation
|
||||||
|
import de.twomartens.timetable.bahnApi.model.dto.BahnStationStop
|
||||||
|
import de.twomartens.timetable.bahnApi.model.dto.BahnStations
|
||||||
|
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
|
||||||
|
import de.twomartens.timetable.bahnApi.property.BahnApiProperties
|
||||||
|
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
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class BahnApiService(
|
||||||
|
private val restTemplate: RestTemplate,
|
||||||
|
private val properties: BahnApiProperties
|
||||||
|
) {
|
||||||
|
fun fetchStations(pattern: String): List<BahnStation> {
|
||||||
|
val requestEntity = buildRequestEntity<BahnStations>()
|
||||||
|
val response = restTemplate.exchange(
|
||||||
|
"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/${pattern}",
|
||||||
|
HttpMethod.GET,
|
||||||
|
requestEntity,
|
||||||
|
BahnStations::class.java
|
||||||
|
)
|
||||||
|
val body = response.body
|
||||||
|
return body?.stations ?: listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchTimetable(eva: String, date: LocalDate, hour: LocalTime): BahnTimetable {
|
||||||
|
val requestEntity = buildRequestEntity<BahnTimetable>()
|
||||||
|
val dateFormatter = DateTimeFormatter.ofPattern("yyMMdd")
|
||||||
|
val timeFormatter = DateTimeFormatter.ofPattern("HH")
|
||||||
|
val response = restTemplate.exchange(
|
||||||
|
"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/" +
|
||||||
|
"${eva}/${date.format(dateFormatter)}/${hour.format(timeFormatter)}",
|
||||||
|
HttpMethod.GET,
|
||||||
|
requestEntity,
|
||||||
|
BahnTimetable::class.java
|
||||||
|
)
|
||||||
|
return response.body!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> buildRequestEntity(): HttpEntity<T> {
|
||||||
|
val headers = HttpHeaders()
|
||||||
|
headers.accept = mutableListOf(MediaType.APPLICATION_XML)
|
||||||
|
headers.contentType = MediaType.APPLICATION_XML
|
||||||
|
headers.set("DB-Client-Id", properties.clientId)
|
||||||
|
headers.set("DB-Api-Key", properties.clientSecret)
|
||||||
|
return HttpEntity<T>(headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user