Browse Source

reworked the CacheController Class

pull/18/head
Jannik 3 years ago
parent
commit
132cf2df0e
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
  1. 65
      src/main/kotlin/org/mosad/thecitadelofricks/APIController.kt
  2. 190
      src/main/kotlin/org/mosad/thecitadelofricks/CacheController.kt
  3. 276
      src/main/kotlin/org/mosad/thecitadelofricks/controller/CacheController.kt
  4. 36
      src/main/kotlin/org/mosad/thecitadelofricks/controller/CachetAPIController.kt

65
src/main/kotlin/org/mosad/thecitadelofricks/APIController.kt

@ -22,9 +22,13 @@
package org.mosad.thecitadelofricks
import org.mosad.thecitadelofricks.CacheController.Companion.courseList
import org.mosad.thecitadelofricks.CacheController.Companion.mensaMenu
import org.mosad.thecitadelofricks.CacheController.Companion.timetableList
import org.mosad.thecitadelofricks.controller.CacheController
import org.mosad.thecitadelofricks.controller.CacheController.Companion.courseList
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getLesson
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getLessonSubjectList
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getTimetable
import org.mosad.thecitadelofricks.controller.CacheController.Companion.mensaMenu
import org.mosad.thecitadelofricks.controller.CacheController.Companion.timetableList
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.RequestMapping
@ -35,22 +39,25 @@ import java.net.URL
import java.time.LocalDateTime
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
@RestController
class APIController {
private val logger: Logger = LoggerFactory.getLogger(APIController::class.java)
private val cache = CacheController()
private val apiVersion = "1.1.3"
private val softwareVersion = "1.1.5"
private val startTime = System.currentTimeMillis() / 1000
private var totalRequests = 0
var totalRequests = 0
private var mensaMenuRequests = 0
private var timetableRequests = ArrayList<TimetableCounter>()
init {
CacheController() // initialize the CacheController
}
// TODO remove this with API version 1.2.0
@Deprecated("courses is replaced by courseList", replaceWith = ReplaceWith("courseList()"))
@RequestMapping("/courses")
fun courses(): CourseList {
@ -80,7 +87,7 @@ class APIController {
logger.info("timetable request at ${LocalDateTime.now()}!")
updateTimetableRequests(courseName)
totalRequests++
return cache.getTimetable(courseName, week)
return getTimetable(courseName, week)
}
@RequestMapping("/lessonSubjectList")
@ -111,49 +118,6 @@ class APIController {
// non direct api functions
/**
* get every explicit lesson in a week
* @param courseName the name of the course to be requested
* @param weekIndex request week number (current week = 0)
* @return a HashSet of explicit lessons for one week
*/
private fun getLessonSubjectList(courseName: String, weekIndex: Int): HashSet<String> {
val lessonSubjectList = ArrayList<String>()
// get every lesson subject for the given week
val flatMap = cache.getTimetable(courseName, weekIndex).timetable.days.flatMap { it.timeslots.asIterable() }
flatMap.forEach {
it.stream().filter { x -> x.lessonSubject.isNotEmpty() }.findAny().ifPresent { x -> lessonSubjectList.add(x.lessonSubject) }
}
return HashSet(lessonSubjectList)
}
/**
* get every explicit lesson in a week
* @param courseName the name of the course to be requested
* @param lessonSubject the lesson subject to be requested
* @param weekIndex request week number (current week = 0)
* @return a ArrayList of every lesson with lessonSubject for one week
*/
private fun getLesson(courseName: String, lessonSubject: String, weekIndex: Int): ArrayList<Lesson> {
val lessonList = ArrayList<Lesson>()
// get all lessons from the weeks timetable
val flatMap = cache.getTimetable(courseName, weekIndex).timetable.days.flatMap { it.timeslots.asIterable() }
flatMap.forEach {
it.forEach { lesson ->
if(lesson.lessonSubject.contains(lessonSubject)) {
lessonList.add(lesson)
}
}
//it.stream().filter { x -> x.lessonSubject.contains(lessonSubject) }.findAny().ifPresent { x -> println("${x.lessonSubject}, ${x.lessonTeacher}") }
}
return lessonList
}
/**
* if a timetable is requested update the request counter
*/
@ -163,6 +127,7 @@ class APIController {
}
timetableRequests.stream().filter { x ->x.courseName == courseName }.findFirst().ifPresent { x -> x.requests++ }
// TODO Java 11
// timetableRequests.stream().filter { it.courseName == courseName }.findFirst().ifPresentOrElse({
// it.requests++
// }, {

190
src/main/kotlin/org/mosad/thecitadelofricks/CacheController.kt

@ -1,190 +0,0 @@
/**
* 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 java.util.*
import kotlin.collections.ArrayList
import kotlin.concurrent.scheduleAtFixedRate
class CacheController {
private val logger: Logger = LoggerFactory.getLogger(CacheController::class.java)
// hso parser links (hardcoded)
private val courseListURL = "https://www.hs-offenburg.de/studium/vorlesungsplaene/"
private val mensaMenuURL = "https://www.swfr.de/de/essen-trinken/speiseplaene/mensa-offenburg/"
private val mensaName = "Offenburg"
// cache objects
companion object{
lateinit var courseList: CourseList
lateinit var mensaMenu: MensaMenu
var timetableList = ArrayList<TimetableCourseWeek>() // this list contains all timetables
}
init {
initUpdates()
scheduledUpdates()
}
/**
* get a timetable, since they may not cached, we need to make sure it's cached, otherwise download
* @param courseName the name of the course to be requested
* @param weekIndex request week number (current week = 0)
* @return the timetable of a course (courseName)
*/
fun getTimetable(courseName: String, weekIndex: Int): TimetableCourseWeek = runBlocking {
val currentTime = System.currentTimeMillis() / 1000
var timetable = TimetableWeek()
var weekNumberYear = 0
// check if the timetable already exists and is up to date
when (timetableList.stream().filter { x -> x.meta.courseName == courseName && x.meta.weekIndex == weekIndex }.findAny().orElse(null)) {
// there is no such course yet, create one
null -> {
val courseLink = courseList.courses.stream().filter { x -> x.courseName == courseName }.findFirst().orElse(null).courseLink
val timetableLink = courseLink.replace("week=0","week=$weekIndex")
val jobTimetable = GlobalScope.async {
timetable = TimetableParser().getTimeTable(timetableLink)
weekNumberYear = TimetableParser().getWeekNumberYear(timetableLink)
}
jobTimetable.await()
timetableList.add(TimetableCourseWeek(TimetableCourseMeta(currentTime, courseName, weekIndex, weekNumberYear, timetableLink), timetable))
logger.info("added new timetable for $courseName, week $weekIndex")
}
}
return@runBlocking timetableList.stream().filter { x -> x.meta.courseName == courseName && x.meta.weekIndex == weekIndex }.findAny().orElse(null)
}
/**
* this function updates the courseList
* during the update process the old data will be returned for a API request
*/
private fun asyncUpdateCourseList() = GlobalScope.launch {
CourseListParser().getCourseLinks(courseListURL)?.let {
courseList = CourseList(CourseMeta(System.currentTimeMillis() / 1000, it.size), it)
}
logger.info("updated courses successful at ${Date(courseList.meta.updateTime * 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 {
val mensaCurrentWeek = MensaParser().getMensaMenu(mensaMenuURL)
val mensaNextWeek = MensaParser().getMensaMenu(MensaParser().getMenuLinkNextWeek(mensaMenuURL))
// only update if we get valid data
if (mensaCurrentWeek != null && mensaNextWeek != null) {
mensaMenu = MensaMenu(MensaMeta(System.currentTimeMillis() / 1000, mensaName), mensaCurrentWeek, mensaNextWeek)
}
logger.info("updated mensamenu successful at ${Date(mensaMenu.meta.updateTime * 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!")
}
/**
* before the APIController is up, get the data fist
* runBlocking: otherwise the api would return no data to requests for a few seconds after startup
*/
private fun initUpdates() = runBlocking {
// get all courses on startup
val jobCourseUpdate = GlobalScope.async {
CourseListParser().getCourseLinks(courseListURL)?.let {
courseList = CourseList(CourseMeta(System.currentTimeMillis() / 1000, it.size), it)
}
}
// get the current and next weeks mensa menus
val jobMensa = GlobalScope.async{
val mensaCurrentWeek = MensaParser().getMensaMenu(mensaMenuURL)
val mensaNextWeek = MensaParser().getMensaMenu(MensaParser().getMenuLinkNextWeek(mensaMenuURL))
// only update if we get valid data
if (mensaCurrentWeek != null && mensaNextWeek != null) {
mensaMenu = MensaMenu(MensaMeta(System.currentTimeMillis() / 1000, mensaName), mensaCurrentWeek, mensaNextWeek)
}
}
jobCourseUpdate.await()
jobMensa.await()
logger.info("init updates successful")
}
/**
* doesn't account the change between winter and summer time!
*
*/
private fun scheduledUpdates() {
val currentTime = System.currentTimeMillis()
val initDelay24h = (86400000 - ((currentTime + 3600000) % 86400000)) + 60000
val initDelay3h = (10800000 - ((currentTime + 3600000) % 10800000)) + 60000
val initDelay1h = (3600000 - ((currentTime + 3600000) % 3600000)) + 60000
// update courseList every 24 hours (time in ms)
Timer().scheduleAtFixedRate(initDelay24h, 86400000) {
asyncUpdateCourseList()
}
// update all already existing timetables every 3 hours (time in ms)
Timer().scheduleAtFixedRate(initDelay3h, 10800000) {
asyncUpdateTimetables()
}
// update courses every hour (time in ms)
Timer().scheduleAtFixedRate(initDelay1h, 3600000) {
asyncUpdateMensa()
}
}
}

276
src/main/kotlin/org/mosad/thecitadelofricks/controller/CacheController.kt

@ -0,0 +1,276 @@
/**
* 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.controller
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.mosad.thecitadelofricks.*
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 java.util.*
import kotlin.collections.ArrayList
import kotlin.concurrent.scheduleAtFixedRate
class CacheController {
private val logger: Logger = LoggerFactory.getLogger(CacheController::class.java)
// hso parser links (hardcoded)
private val courseListURL = "https://www.hs-offenburg.de/studium/vorlesungsplaene/"
private val mensaMenuURL = "https://www.swfr.de/de/essen-trinken/speiseplaene/mensa-offenburg/"
private val mensaName = "Offenburg"
init {
initUpdates()
scheduledUpdates()
//CachetAPIController.postTotalRequests(5)
}
// cache objects
companion object{
private val logger: Logger = LoggerFactory.getLogger(CacheController::class.java)
lateinit var courseList: CourseList
lateinit var mensaMenu: MensaMenu
var timetableList = ArrayList<TimetableCourseWeek>() // this list contains all timetables
/**
* get a timetable, since they may not cached, we need to make sure it's cached, otherwise download
* @param courseName the name of the course to be requested
* @param weekIndex request week number (current week = 0)
* @return the timetable of a course (courseName)
*/
fun getTimetable(courseName: String, weekIndex: Int): TimetableCourseWeek = runBlocking {
val currentTime = System.currentTimeMillis() / 1000
var timetable = TimetableWeek()
var weekNumberYear = 0
// check if the timetable already exists and is up to date
when (timetableList.stream().filter { x -> x.meta.courseName == courseName && x.meta.weekIndex == weekIndex }.findAny().orElse(null)) {
// there is no such course yet, create one
null -> {
val courseLink = courseList.courses.stream().filter { x -> x.courseName == courseName }.findFirst().orElse(null).courseLink
val timetableLink = courseLink.replace("week=0","week=$weekIndex")
val jobTimetable = GlobalScope.async {
timetable = TimetableParser().getTimeTable(timetableLink)
weekNumberYear = TimetableParser().getWeekNumberYear(timetableLink)
}
jobTimetable.await()
timetableList.add(
TimetableCourseWeek(
TimetableCourseMeta(
currentTime,
courseName,
weekIndex,
weekNumberYear,
timetableLink
),
timetable
)
)
logger.info("added new timetable for $courseName, week $weekIndex")
}
}
return@runBlocking timetableList.stream().filter { x -> x.meta.courseName == courseName && x.meta.weekIndex == weekIndex }.findAny().orElse(null)
}
/**
* get every explicit lesson in a week
* @param courseName the name of the course to be requested
* @param weekIndex request week number (current week = 0)
* @return a HashSet of explicit lessons for one week
*/
fun getLessonSubjectList(courseName: String, weekIndex: Int): HashSet<String> = runBlocking {
val lessonSubjectList = ArrayList<String>()
// get every lesson subject for the given week
val flatMap = getTimetable(courseName, weekIndex).timetable.days.flatMap { it.timeslots.asIterable() }
flatMap.forEach {
it.stream().filter { x -> x.lessonSubject.isNotEmpty() }.findAny().ifPresent { x -> lessonSubjectList.add(x.lessonSubject) }
}
return@runBlocking HashSet(lessonSubjectList)
}
/**
* get every explicit lesson in a week
* @param courseName the name of the course to be requested
* @param lessonSubject the lesson subject to be requested
* @param weekIndex request week number (current week = 0)
* @return a ArrayList of every lesson with lessonSubject for one week
*/
fun getLesson(courseName: String, lessonSubject: String, weekIndex: Int): ArrayList<Lesson> {
val lessonList = ArrayList<Lesson>()
// get all lessons from the weeks timetable
val flatMap = getTimetable(courseName, weekIndex).timetable.days.flatMap { it.timeslots.asIterable() }
flatMap.forEach {
it.forEach { lesson ->
if(lesson.lessonSubject.contains(lessonSubject)) {
lessonList.add(lesson)
}
}
// TODO Java 11
//it.stream().filter { x -> x.lessonSubject.contains(lessonSubject) }.findAny().ifPresent { x -> println("${x.lessonSubject}, ${x.lessonTeacher}") }
}
return lessonList
}
}
/**
* this function updates the courseList
* during the update process the old data will be returned for a API request
*/
private fun asyncUpdateCourseList() = GlobalScope.launch {
CourseListParser().getCourseLinks(courseListURL)?.let {
courseList =
CourseList(
CourseMeta(
System.currentTimeMillis() / 1000,
it.size
), it
)
}
logger.info("updated courses successful at ${Date(courseList.meta.updateTime * 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 {
val mensaCurrentWeek = MensaParser().getMensaMenu(mensaMenuURL)
val mensaNextWeek = MensaParser().getMensaMenu(MensaParser().getMenuLinkNextWeek(mensaMenuURL))
// only update if we get valid data
if (mensaCurrentWeek != null && mensaNextWeek != null) {
mensaMenu =
MensaMenu(
MensaMeta(
System.currentTimeMillis() / 1000,
mensaName
), mensaCurrentWeek, mensaNextWeek
)
}
logger.info("updated mensamenu successful at ${Date(mensaMenu.meta.updateTime * 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!")
}
/**
* before the APIController is up, get the data fist
* runBlocking: otherwise the api would return no data to requests for a few seconds after startup
*/
private fun initUpdates() = runBlocking {
// get all courses on startup
val jobCourseUpdate = GlobalScope.async {
CourseListParser().getCourseLinks(courseListURL)?.let {
courseList =
CourseList(
CourseMeta(
System.currentTimeMillis() / 1000,
it.size
), it
)
}
}
// get the current and next weeks mensa menus
val jobMensa = GlobalScope.async{
val mensaCurrentWeek = MensaParser().getMensaMenu(mensaMenuURL)
val mensaNextWeek = MensaParser().getMensaMenu(MensaParser().getMenuLinkNextWeek(mensaMenuURL))
// only update if we get valid data
if (mensaCurrentWeek != null && mensaNextWeek != null) {
mensaMenu =
MensaMenu(
MensaMeta(
System.currentTimeMillis() / 1000,
mensaName
), mensaCurrentWeek, mensaNextWeek
)
}
}
jobCourseUpdate.await()
jobMensa.await()
logger.info("init updates successful")
}
/**
* doesn't account the change between winter and summer time!
*
*/
private fun scheduledUpdates() {
val currentTime = System.currentTimeMillis()
val initDelay24h = (86400000 - ((currentTime + 3600000) % 86400000)) + 60000
val initDelay3h = (10800000 - ((currentTime + 3600000) % 10800000)) + 60000
val initDelay1h = (3600000 - ((currentTime + 3600000) % 3600000)) + 60000
// update courseList every 24 hours (time in ms)
Timer().scheduleAtFixedRate(initDelay24h, 86400000) {
asyncUpdateCourseList()
}
// update all already existing timetables every 3 hours (time in ms)
Timer().scheduleAtFixedRate(initDelay3h, 10800000) {
asyncUpdateTimetables()
}
// update courses every hour (time in ms)
Timer().scheduleAtFixedRate(initDelay1h, 3600000) {
asyncUpdateMensa()
}
// post to status.mosad.xyz every hour
Timer().scheduleAtFixedRate(initDelay1h, 3600000) {
// TODO
}
}
}

36
src/main/kotlin/org/mosad/thecitadelofricks/controller/CachetAPIController.kt

@ -0,0 +1,36 @@
package org.mosad.thecitadelofricks.controller
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.net.HttpURLConnection
import java.net.URL
import java.io.OutputStreamWriter
class CachetAPIController {
companion object {
private val logger: Logger = LoggerFactory.getLogger(CachetAPIController::class.java)
fun postTotalRequests(totalRequests: Int) {
logger.info("sending post request ...")
val currentTime = System.currentTimeMillis() / 1000
val data = mapOf(Pair("value", totalRequests.toString()), Pair("timestamp", currentTime.toString()))
val url = URL("https://status.mosad.xyz/api/v1/metrics/1/points")
val connection = url.openConnection() as HttpURLConnection
connection.setRequestProperty("X-Cachet-Token", "h0103Wix5xhhNH5tk5KU")
connection.requestMethod = "POST"
connection.doOutput = true
val writer = OutputStreamWriter(connection.outputStream)
writer.write(data.toString())
writer.flush()
writer.close()
}
}
}
Loading…
Cancel
Save

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