You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

240 lines
9.1 KiB

/**
* TheCitadelofRicks
*
* Copyright 2019 <seil0@mosad.xyz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
package org.mosad.thecitadelofricks
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.mosad.thecitadelofricks.hsoparser.CourseListParser
import org.mosad.thecitadelofricks.hsoparser.MensaParser
import org.mosad.thecitadelofricks.hsoparser.TimetableParser
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.net.HttpURLConnection
import java.net.URL
import java.time.LocalDateTime
import java.util.*
import kotlin.collections.ArrayList
import kotlin.concurrent.scheduleAtFixedRate
@RestController
class APIController {
// Controller stuff
var logger: Logger = LoggerFactory.getLogger(APIController::class.java)
private var requestCount = 0
private val startTime = System.currentTimeMillis() / 1000
private val softwareVersion = "1.0.2"
private val apiVersion = "1.0.1"
// hso parser links (hardcoded)
private val mensaLink = "https://www.swfr.de/de/essen-trinken/speiseplaene/mensa-offenburg/"
private val mensaName = "Offenburg"
// cache objects
private var coursesLinkList = ArrayList<Course>()
private var coursesLastUpdate: Long = 0
private var timetableList = ArrayList<TimetableCourseWeek>() // this list contains all timetables
private var mensaCurrentWeek = MensaWeek()
private var mensaNextWeek = MensaWeek()
private var mensaLastUpdate: Long = 0
init {
initUpdates() // without this 5-10 seconds after startup the response will be empty
val currentTime = System.currentTimeMillis()
val delay24h = (86400000 - ((currentTime + 3600000) % 86400000)) + 60000
val delay3h = (10800000 - ((currentTime + 3600000) % 10800000)) + 60000
// update courses every 24 hours (time in ms)
Timer().scheduleAtFixedRate(delay24h, 86400000) {
asyncUpdateCourses()
}
// update courses every 3 hours (time in ms)
Timer().scheduleAtFixedRate(delay3h, 10800000) {
asyncUpdateMensa()
}
// update all already existing timetables every 3 hours (time in ms)
Timer().scheduleAtFixedRate(delay3h, 10800000) {
asyncUpdateTimetables()
}
}
@RequestMapping("/courses")
fun courses(): CoursesList {
logger.info("courses request at ${LocalDateTime.now()}!")
requestCount++
return CoursesList(CoursesMeta(coursesLastUpdate, coursesLinkList.size), coursesLinkList)
}
@RequestMapping("/mensamenu")
fun mensamenu(): Mensa {
logger.info("mensamenu request at ${LocalDateTime.now()}!")
requestCount++
return Mensa(MensaMeta(mensaLastUpdate, mensaName), mensaCurrentWeek, mensaNextWeek)
}
@RequestMapping("/timetable")
fun timetable(
@RequestParam(value = "courseName", defaultValue = "AI4") courseName: String,
@RequestParam(value = "week", defaultValue = "0") week: Int
): TimetableCourseWeek {
logger.info("timetable request at ${LocalDateTime.now()}!")
requestCount++
checkTimetableCourse(courseName, week) // check if we need to update and perform the update if so
return timetableList.stream().filter { x -> x.meta.courseName == courseName && x.meta.week == week }.findAny().orElse(null)
}
@RequestMapping("/status")
fun status(): Status {
val currentTime = System.currentTimeMillis() / 1000
val minutes = (currentTime - startTime) % 3600 / 60
val hours = (currentTime - startTime) % 86400 / 3600
val days = (currentTime - startTime) / 86400
var hsoCode = 999
var swfrCode = 999
logger.info("status request at ${LocalDateTime.now()}!")
try {
val hsoURL = URL("https://www.hs-offenburg.de/")
var connection = hsoURL.openConnection() as HttpURLConnection
connection.requestMethod = "HEAD"
hsoCode = connection.responseCode
val swfrURL = URL("https://www.swfr.de/")
connection = swfrURL.openConnection() as HttpURLConnection
swfrCode = connection.responseCode
} catch (e: Exception) {
logger.error("Error while fetching url response codes!", e)
}
return Status(
LocalDateTime.now(),
"$days days, $hours:$minutes",
apiVersion,
softwareVersion,
requestCount,
Date(coursesLastUpdate * 1000),
Date(mensaLastUpdate * 1000),
hsoCode,
swfrCode
)
}
/**
* this function updates the courses list
* during the update process the old data will be returned for a API request
*/
private fun asyncUpdateCourses() = GlobalScope.launch {
coursesLinkList = CourseListParser().getCourseLinks()
coursesLastUpdate = System.currentTimeMillis() / 1000
logger.info("updated courses successful at ${Date(coursesLastUpdate * 1000)}")
}
/**
* this function updates the mensa menu list
* during the update process the old data will be returned for a API request
*/
private fun asyncUpdateMensa() = GlobalScope.launch {
mensaCurrentWeek = MensaParser().getMensaMenu(mensaLink)
mensaNextWeek = MensaParser().getMensaMenu(MensaParser().getMenuLinkNextWeek(mensaLink))
mensaLastUpdate = System.currentTimeMillis() / 1000
logger.info("updated mensamenu successful at ${Date(mensaLastUpdate * 1000)}")
}
/**
* this function updates all existing timetables
* during the update process the old data will be returned for a API request
*/
private fun asyncUpdateTimetables() = GlobalScope.launch {
timetableList.forEach { timetableCourse ->
val updateURL = timetableCourse.meta.link
timetableCourse.timetable = TimetableParser().getTimeTable(updateURL)
timetableCourse.meta.updateTime = System.currentTimeMillis() / 1000
}
logger.info("updated ${timetableList.size} timetables successful!")
}
/**
* this function checks if we need to update the timetable for a given course and if so does it
* during the update process the old data will be returned for a API request
* update if the last update was 6 hours ago
*/
private fun checkTimetableCourse(courseName: String, week: Int) = runBlocking {
val currentTime = System.currentTimeMillis() / 1000
var timetable = TimetableWeek()
// check if the timetable already exists and is up to date
val result = timetableList.stream().filter { x -> x.meta.courseName == courseName && x.meta.week == week }.findAny().orElse(null)
when (result) {
// there is no such course yet, create one
null -> {
val courseLink = coursesLinkList.stream().filter { x -> x.courseName == courseName }.findFirst().orElse(null).courseLink
val timetableMeta = TimetableCourseMeta(currentTime, courseName, week, courseLink.replace("week=0","week=$week"))
val jobTimetable = GlobalScope.async {
timetable = TimetableParser().getTimeTable(timetableMeta.link)
}
jobTimetable.await()
timetableList.add(TimetableCourseWeek(timetableMeta, timetable))
logger.info("added new timetable for $courseName, week $week")
}
}
}
private fun initUpdates() = runBlocking {
// get all courses on startup
val jobCourseUpdate = GlobalScope.async{
coursesLinkList = CourseListParser().getCourseLinks()
coursesLastUpdate = System.currentTimeMillis() / 1000
}
// get the current and next weeks mensa menus
val jobCurrentMensa = GlobalScope.async{
mensaCurrentWeek = MensaParser().getMensaMenu(mensaLink)
}
val jobNextMensa = GlobalScope.async{
mensaCurrentWeek = MensaParser().getMensaMenu(mensaLink)
mensaLastUpdate = System.currentTimeMillis() / 1000
}
jobCourseUpdate.await()
jobCurrentMensa.await()
jobNextMensa.await()
logger.info("init updates successful")
}
}

Du besuchst diese Seite mit einem veralteten IPv4-Internetzugang. Möglicherweise treten in Zukunft Probleme mit der Erreichbarkeit und Performance auf. Bitte frage deinen Internetanbieter oder Netzwerkadministrator nach IPv6-Unterstützung.
You are visiting this site with an outdated IPv4 internet access. You may experience problems with accessibility and performance in the future. Please ask your ISP or network administrator for IPv6 support.
Weitere Infos | More Information
Klicke zum schließen | Click to close