@ -22,28 +22,37 @@
package org.mosad.thecitadelofricks
import kotlinx.coroutines.*
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.lang.Exception
import java.net.*
import java.net.HttpURLConnection
import java.net.URL
import java.time.LocalDateTime
import java.util.*
import kotlin.concurrent.scheduleAtFixedRate
import java.time.LocalDateTime
@RestController
class APIController {
//private val logger = LoggerFactory.getLogger(DemoApplication::class.java)
// Controller stuff
var logger : Logger = LoggerFactory . getLogger ( APIController :: class . java )
private val startTime = System . currentTimeMillis ( ) / 1000
private val apiVersion = " 1.0.0 "
// 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
@ -62,36 +71,45 @@ class APIController {
asyncUpdateCourses ( )
}
// update courses every 6 hours (time in ms)
Timer ( ) . scheduleAtFixedRate ( 0 , 216 00000) {
// update courses every 3 hours (time in ms)
Timer ( ) . scheduleAtFixedRate ( 0 , 108 00000) {
asyncUpdateMensa ( )
}
// update all already existing timetables every 3 hours (time in ms)
Timer ( ) . scheduleAtFixedRate ( 0 , 10800000 ) {
asyncUpdateTimetables ( )
}
}
@RequestMapping ( " /courses " )
fun courses ( ) : CoursesList {
println ( " courses request at " + System . currentTimeMillis ( ) / 1000 + " ! " )
logger . info ( " courses request at ${LocalDateTime.now()} ! " )
return CoursesList ( CoursesMeta ( coursesLinkList . size , coursesLastUpdate ) , coursesLinkList )
}
@RequestMapping ( " /mensamenu " )
fun mensamenu ( ) : Mensa {
println ( " mensamenu request at " + System . currentTimeMillis ( ) / 1000 + " ! " )
logger . info ( " mensamenu request at ${LocalDateTime.now()} ! " )
return Mensa ( MensaMeta ( mensaName , mensaLastUpdate ) , mensaCurrentWeek , mensaNextWeek )
}
@RequestMapping ( " /timetable " )
fun timetable ( @RequestParam ( value = " courseName " , defaultValue = " AI4 " ) courseName : String ) : TimetableCourse {
println ( " timetable request at " + System . currentTimeMillis ( ) / 1000 + " ! " )
update TimetableCourse( courseName ) // check if we need to update and perform the update if so
logger . info ( " timetable request at ${LocalDateTime.now()} ! " )
check TimetableCourse( courseName ) // check if we need to update and perform the update if so
return timetableList . stream ( ) . filter { x -> x . meta . courseName == courseName } . findAny ( ) . orElse ( null )
}
@RequestMapping ( " /status " )
fun status ( ) : Status {
println ( " status request at " + System . currentTimeMillis ( ) / 1000 + " ! " )
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/ " )
@ -102,35 +120,55 @@ class APIController {
connection = swfrURL . openConnection ( ) as HttpURLConnection
swfrCode = connection . responseCode
} catch ( e : Exception ) {
// do some error handling
logger . error ( " Error while fetching url response codes! " , e )
}
return Status ( LocalDateTime . now ( ) , Date ( coursesLastUpdate * 1000 ) , Date ( mensaLastUpdate * 1000 ) , hsoCode , swfrCode )
return Status (
LocalDateTime . now ( ) ,
" $days days, $hours : $minutes " ,
apiVersion ,
Date ( coursesLastUpdate * 1000 ) ,
Date ( mensaLastUpdate * 1000 ) ,
hsoCode ,
swfrCode
)
}
/ * *
* checks if we need to update the courses list and if so does it async
* this function updates the courses list
* during the update process the old data will be returned for a API request
* update if the last update was 24 hours ago
* /
private fun asyncUpdateCourses ( ) = GlobalScope . launch {
coursesLinkList = CourseListParser ( ) . getCourseLinks ( )
coursesLastUpdate = System . currentTimeMillis ( ) / 1000
println ( " updated courses successful at $coursesLastUpdate " )
logger . info ( " updated courses successful at ${Date( coursesLastUpdate * 1000)} " )
}
/ * *
* this function checks if we need to update the mensa menu list and if so does i t
* this function updates the mensa menu list
* 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 asyncUpdateMensa ( ) = GlobalScope . launch {
mensaCurrentWeek = MensaParser ( ) . getMensaMenu ( mensaLink )
mensaNextWeek = MensaParser ( ) . getMensaMenu ( MensaParser ( ) . getMenuLinkNextWeek ( mensaLink ) )
mensaLastUpdate = System . currentTimeMillis ( ) / 1000
println ( " updated mensamenu successful at $mensaLastUpdate " )
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 . courseLink
timetableCourse . currentWeek = TimetableParser ( ) . getTimeTable ( updateURL )
timetableCourse . nextWeek = TimetableParser ( ) . getTimeTable ( updateURL . replace ( " week=0 " , " week=1 " ) )
timetableCourse . meta . time = System . currentTimeMillis ( ) / 1000
}
logger . info ( " updated ${timetableList.size} timetables successful! " )
}
/ * *
@ -138,16 +176,16 @@ class APIController {
* 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 update TimetableCourse( courseName : String ) = runBlocking {
private fun check TimetableCourse( courseName : String ) = runBlocking {
val currentTime = System . currentTimeMillis ( ) / 1000
var currentWeek = TimetableWeek ( )
var nextWeek = TimetableWeek ( )
// check if the timetable already exists and is up to date
val result = timetableList . stream ( ) . filter { x -> x . meta . courseName == courseName } . findAny ( ) . orElse ( null )
when {
when ( result ) {
// there is no such course yet, create one
result == null -> {
null -> {
val courseLink = coursesLinkList . stream ( ) . filter { x -> x . courseName == courseName } . findFirst ( ) . orElse ( null ) . courseLink
val timetableMeta = TimetableMeta ( courseName , courseLink , currentTime )
@ -163,21 +201,8 @@ class APIController {
jobNext . await ( )
timetableList . add ( TimetableCourse ( timetableMeta , currentWeek , nextWeek ) )
logger . info ( " added new timetable for $courseName " )
}
// update
( currentTime - result . meta . time ) > 21600 -> {
val index = timetableList . indexOf ( result )
println ( " update $courseName wit index: $index " )
GlobalScope . async {
val courseLink = coursesLinkList . stream ( ) . filter { x -> x . courseName == courseName } . findFirst ( ) . orElse ( null ) . courseLink
timetableList [ index ] . currentWeek = TimetableParser ( ) . getTimeTable ( courseLink )
timetableList [ index ] . nextWeek = TimetableParser ( ) . getTimeTable ( courseLink . replace ( " week=0 " , " week=1 " ) )
timetableList [ index ] . meta . time = currentTime
}
}
else -> println ( " timetable for $courseName is up to date " )
}
}
@ -201,6 +226,8 @@ class APIController {
jobCourseUpdate . await ( )
jobCurrentMensa . await ( )
jobNextMensa . await ( )
logger . info ( " init updates successful " )
}
}