From f2c1f2bc586601685b25a9c9805c073ab2fe01a1 Mon Sep 17 00:00:00 2001 From: Seil0 Date: Wed, 13 Mar 2019 19:12:05 +0100 Subject: [PATCH] update all existing courses every 3 hours & some clean up --- build.gradle | 2 +- .../mosad/thecitadelofricks/APIController.kt | 103 +++++++++++------- .../org/mosad/thecitadelofricks/DataTypes.kt | 20 +++- .../hsoparser/MensaParser.kt | 5 +- .../hsoparser/TimetableParser.kt | 2 +- src/main/resources/application.properties | 4 + 6 files changed, 89 insertions(+), 47 deletions(-) create mode 100644 src/main/resources/application.properties diff --git a/build.gradle b/build.gradle index d1d3e27..0aaa34a 100644 --- a/build.gradle +++ b/build.gradle @@ -39,4 +39,4 @@ compileTestKotlin { } group 'org.mosad' -version '1.0-SNAPSHOT' +version '1.0.0' diff --git a/src/main/kotlin/org/mosad/thecitadelofricks/APIController.kt b/src/main/kotlin/org/mosad/thecitadelofricks/APIController.kt index d5d07a1..8f710fa 100644 --- a/src/main/kotlin/org/mosad/thecitadelofricks/APIController.kt +++ b/src/main/kotlin/org/mosad/thecitadelofricks/APIController.kt @@ -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() private var coursesLastUpdate: Long = 0 @@ -62,36 +71,45 @@ class APIController { asyncUpdateCourses() } - // update courses every 6 hours (time in ms) - Timer().scheduleAtFixedRate(0, 21600000) { + // update courses every 3 hours (time in ms) + Timer().scheduleAtFixedRate(0, 10800000) { 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 + "!") - updateTimetableCourse(courseName) // check if we need to update and perform the update if so + logger.info("timetable request at ${LocalDateTime.now()}!") + checkTimetableCourse(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 it + * 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 updateTimetableCourse(courseName: String) = runBlocking { + private fun checkTimetableCourse(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") } } \ No newline at end of file diff --git a/src/main/kotlin/org/mosad/thecitadelofricks/DataTypes.kt b/src/main/kotlin/org/mosad/thecitadelofricks/DataTypes.kt index f76466b..ccc0d56 100644 --- a/src/main/kotlin/org/mosad/thecitadelofricks/DataTypes.kt +++ b/src/main/kotlin/org/mosad/thecitadelofricks/DataTypes.kt @@ -44,9 +44,15 @@ data class MensaMeta(val mensaName: String, val time: Long) data class Mensa(val meta: MensaMeta, val currentWeek: MensaWeek, val nextWeek: MensaWeek) // data classes for the timetable part -data class Lesson(val lessonID: String, val lessonSubject: String, val lessonTeacher: String, val lessonRoom:String, val lessonRemark: String) +data class Lesson( + val lessonID: String, + val lessonSubject: String, + val lessonTeacher: String, + val lessonRoom: String, + val lessonRemark: String +) -data class TimetableDay( val timeslots: Array> = Array(6) { ArrayList()}) +data class TimetableDay(val timeslots: Array> = Array(6) { ArrayList() }) data class TimetableWeek(val days: Array = Array(6) { TimetableDay() }) @@ -56,4 +62,12 @@ data class TimetableCourse(val meta: TimetableMeta, var currentWeek: TimetableWe // data classes for the status part -data class Status(val time: LocalDateTime, val coursesLastUpdate: Date, val mensaLastUpdate: Date, val hsoResponseCode: Int, val swfrResponseCode: Int) \ No newline at end of file +data class Status( + val time: LocalDateTime, + val uptime: String, + val apiVersion: String, + val coursesLastUpdate: Date, + val mensaLastUpdate: Date, + val hsoResponseCode: Int, + val swfrResponseCode: Int +) \ No newline at end of file diff --git a/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/MensaParser.kt b/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/MensaParser.kt index 8136604..3e48318 100644 --- a/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/MensaParser.kt +++ b/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/MensaParser.kt @@ -43,16 +43,13 @@ class MensaParser { val heading = day.select("h4")[mealIndex].text() val parts = ArrayList(meal.html().substringBefore("
\n").replace("\n", "").split("
")) val additives = meal.select("span.show-with-allergenes").text() - parts.removeIf { x -> x.isEmpty()|| x.isBlank() } + parts.removeIf { x -> x.isEmpty() || x.isBlank() } mealWeekList.days[dayIndex].meals.add(Meal(strDay, heading, parts, additives)) } } - // Mon to Sat (0 - 5) - //println(mealWeekList.days[4]) - return mealWeekList } diff --git a/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/TimetableParser.kt b/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/TimetableParser.kt index a57ee13..722cc4f 100644 --- a/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/TimetableParser.kt +++ b/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/TimetableParser.kt @@ -51,7 +51,7 @@ class TimetableParser { var day = 0 // elements are now all lessons, including empty ones - row.select("td.lastcol, td[style]").forEachIndexed {elementIndex, element -> + row.select("td.lastcol, td[style]").forEachIndexed { elementIndex, element -> // if there is a lecture with rowspan="2", we need to shift everything by one to the left. This is stupid and ugly there needs to bee an API if ((sDay > -1 && sRow > -1) && (sDay == day && ((sRow + 1) == rowIndex))) { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..a627662 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,4 @@ +# Specify the level for spring boot and hibernate's loggers +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.org.springframework.web = INFO +#logging.level.org.hibernate = ERROR \ No newline at end of file