Add API for room occupancy
This commit is contained in:
parent
b78d28a6d3
commit
1a768e1188
|
@ -26,12 +26,16 @@ import org.mosad.thecitadelofricks.controller.CacheController
|
||||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.courseList
|
import org.mosad.thecitadelofricks.controller.CacheController.Companion.courseList
|
||||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getLesson
|
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getLesson
|
||||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getLessonSubjectList
|
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getLessonSubjectList
|
||||||
|
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getRoomSchedule
|
||||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getTimetable
|
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getTimetable
|
||||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.mensaMenu
|
import org.mosad.thecitadelofricks.controller.CacheController.Companion.mensaMenu
|
||||||
|
import org.mosad.thecitadelofricks.controller.CacheController.Companion.roomList
|
||||||
import org.mosad.thecitadelofricks.controller.StartupController
|
import org.mosad.thecitadelofricks.controller.StartupController
|
||||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.getStatus
|
import org.mosad.thecitadelofricks.controller.StatusController.Companion.getStatus
|
||||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateCourseListRequests
|
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateCourseListRequests
|
||||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateMensaMenuRequests
|
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateMensaMenuRequests
|
||||||
|
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateRoomListRequests
|
||||||
|
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateRoomScheduleRequests
|
||||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateTimetableRequests
|
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateTimetableRequests
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
@ -48,7 +52,7 @@ class APIController {
|
||||||
private val logger: Logger = LoggerFactory.getLogger(APIController::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(APIController::class.java)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val apiVersion = "1.3.0"
|
const val apiVersion = "1.4.0"
|
||||||
const val softwareVersion = "1.3.1"
|
const val softwareVersion = "1.3.1"
|
||||||
val startTime = System.currentTimeMillis() / 1000
|
val startTime = System.currentTimeMillis() / 1000
|
||||||
}
|
}
|
||||||
|
@ -104,6 +108,23 @@ class APIController {
|
||||||
return getLesson(courseName, lessonSubject, week)
|
return getLesson(courseName, lessonSubject, week)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/roomList")
|
||||||
|
fun roomList(): RoomsListRet {
|
||||||
|
logger.info("roomList request at ${LocalDateTime.now()}!")
|
||||||
|
updateRoomListRequests()
|
||||||
|
return RoomsListRet(roomList.meta, ArrayList(roomList.rooms.values))
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/roomSchedule")
|
||||||
|
fun roomSchedule(
|
||||||
|
@RequestParam(value = "room", defaultValue = "B040") roomName: String,
|
||||||
|
@RequestParam(value = "week", defaultValue = "0") week: Int
|
||||||
|
): RoomScheduleWeekRet {
|
||||||
|
logger.info("roomSchedule request at ${LocalDateTime.now()}!")
|
||||||
|
updateRoomScheduleRequests(roomName)
|
||||||
|
return getRoomSchedule(roomName, week)
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping("/status")
|
@RequestMapping("/status")
|
||||||
fun status(): Status {
|
fun status(): Status {
|
||||||
logger.info("status request at ${LocalDateTime.now()}!")
|
logger.info("status request at ${LocalDateTime.now()}!")
|
||||||
|
|
|
@ -65,6 +65,18 @@ data class TimetableCourseMeta(var updateTime: Long = 0, val courseName: String
|
||||||
|
|
||||||
data class TimetableCourseWeek(val meta: TimetableCourseMeta = TimetableCourseMeta(), var timetable: TimetableWeek = TimetableWeek())
|
data class TimetableCourseWeek(val meta: TimetableCourseMeta = TimetableCourseMeta(), var timetable: TimetableWeek = TimetableWeek())
|
||||||
|
|
||||||
|
// data classes for the room occupancy part
|
||||||
|
data class Room(val roomName: String, val roomLink: String)
|
||||||
|
|
||||||
|
data class RoomsMeta(val updateTime: Long = 0, val totalRooms: Int = 0)
|
||||||
|
|
||||||
|
data class RoomsList(val meta: RoomsMeta = RoomsMeta(), val rooms: SortedMap<String, Room>)
|
||||||
|
data class RoomsListRet(val meta: RoomsMeta = RoomsMeta(), val rooms: ArrayList<Room> = ArrayList())
|
||||||
|
|
||||||
|
data class RoomScheduleMeta(var updateTime: Long = 0, val roomName: String = "", val weekIndex: Int = 0, var weekNumberYear: Int = 0, var year: Int = 0, val link: String = "")
|
||||||
|
|
||||||
|
data class RoomScheduleWeekRet(val meta: RoomScheduleMeta = RoomScheduleMeta(), var timetable: TimetableWeek = TimetableWeek())
|
||||||
|
|
||||||
|
|
||||||
// data classes for the status part
|
// data classes for the status part
|
||||||
|
|
||||||
|
@ -79,6 +91,10 @@ data class Status(
|
||||||
val timetableRequests: HashMap<String, Int>,
|
val timetableRequests: HashMap<String, Int>,
|
||||||
val timetableListSize: Int,
|
val timetableListSize: Int,
|
||||||
val coursesLastUpdate: Date,
|
val coursesLastUpdate: Date,
|
||||||
|
val roomListRequests: Int,
|
||||||
|
val roomScheduleRequests: HashMap<String, Int>,
|
||||||
|
val roomScheduleListSize: Int,
|
||||||
|
val roomsLastUpdate: Date,
|
||||||
val mensaLastUpdate: Date,
|
val mensaLastUpdate: Date,
|
||||||
val hsoResponseCode: Int,
|
val hsoResponseCode: Int,
|
||||||
val swfrResponseCode: Int
|
val swfrResponseCode: Int
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.jsoup.Jsoup
|
||||||
import org.mosad.thecitadelofricks.*
|
import org.mosad.thecitadelofricks.*
|
||||||
import org.mosad.thecitadelofricks.hsoparser.CourseListParser
|
import org.mosad.thecitadelofricks.hsoparser.CourseListParser
|
||||||
import org.mosad.thecitadelofricks.hsoparser.MensaParser
|
import org.mosad.thecitadelofricks.hsoparser.MensaParser
|
||||||
|
import org.mosad.thecitadelofricks.hsoparser.RoomListParser
|
||||||
import org.mosad.thecitadelofricks.hsoparser.TimetableParser
|
import org.mosad.thecitadelofricks.hsoparser.TimetableParser
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
@ -55,6 +56,8 @@ class CacheController {
|
||||||
var courseList = CoursesList(CoursesMeta(), sortedMapOf())
|
var courseList = CoursesList(CoursesMeta(), sortedMapOf())
|
||||||
var mensaMenu = MensaMenu(MensaMeta(0, ""), MensaWeek(), MensaWeek())
|
var mensaMenu = MensaMenu(MensaMeta(0, ""), MensaWeek(), MensaWeek())
|
||||||
var timetableList = ConcurrentHashMap<String, TimetableCourseWeek>() // this list contains all timetables
|
var timetableList = ConcurrentHashMap<String, TimetableCourseWeek>() // this list contains all timetables
|
||||||
|
var roomList = RoomsList(RoomsMeta(), sortedMapOf())
|
||||||
|
var roomScheduleList = ConcurrentHashMap<String, RoomScheduleWeekRet>() // this list contains all room schedules
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get a timetable, since they may not be cached, we need to make sure it's cached, otherwise download
|
* get a timetable, since they may not be cached, we need to make sure it's cached, otherwise download
|
||||||
|
@ -151,6 +154,40 @@ class CacheController {
|
||||||
return lessonList
|
return lessonList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a room schedule.
|
||||||
|
* Since they may not be cached, we need to make sure it's cached. Download the schedule if it is not cached.
|
||||||
|
* @param roomName the name of the room to be requested
|
||||||
|
* @param weekIndex request week number (current week = 0)
|
||||||
|
* @return room schedule of the room (Type: [RoomScheduleWeekRet])
|
||||||
|
*/
|
||||||
|
fun getRoomSchedule(roomName: String, weekIndex: Int): RoomScheduleWeekRet {
|
||||||
|
val key = "$roomName-$weekIndex"
|
||||||
|
return if (roomScheduleList.containsKey(key)) {
|
||||||
|
roomScheduleList[key]!!
|
||||||
|
} else {
|
||||||
|
val roomScheduleLink = roomList.rooms[roomName]
|
||||||
|
?.roomLink
|
||||||
|
?.replace("week=0", "week=$weekIndex") ?: ""
|
||||||
|
val currentTime = System.currentTimeMillis() / 1000
|
||||||
|
|
||||||
|
val roomScheduleParser = TimetableParser(roomScheduleLink)
|
||||||
|
val calendarWeek = roomScheduleParser.parseCalendarWeek()
|
||||||
|
val roomSchedule = roomScheduleParser.parseTimeTable()
|
||||||
|
|
||||||
|
RoomScheduleWeekRet(
|
||||||
|
RoomScheduleMeta(
|
||||||
|
currentTime,
|
||||||
|
roomName,
|
||||||
|
weekIndex,
|
||||||
|
calendarWeek?.week ?: 0,
|
||||||
|
calendarWeek?.year ?: 0,
|
||||||
|
roomScheduleLink
|
||||||
|
), roomSchedule ?: TimetableWeek()
|
||||||
|
).also { if (roomSchedule != null) roomScheduleList[key] = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// private cache functions
|
// private cache functions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,7 +195,7 @@ class CacheController {
|
||||||
* during the update process the old data will be returned for an API request
|
* during the update process the old data will be returned for an API request
|
||||||
*/
|
*/
|
||||||
private fun asyncUpdateCourseList() = CoroutineScope(Dispatchers.IO).launch {
|
private fun asyncUpdateCourseList() = CoroutineScope(Dispatchers.IO).launch {
|
||||||
CourseListParser().getCourseLinks(StartupController.courseListURL)?.let {
|
CourseListParser().getLinks(StartupController.courseListURL)?.let {
|
||||||
courseList = CoursesList(CoursesMeta(System.currentTimeMillis() / 1000, it.size), it.toSortedMap())
|
courseList = CoursesList(CoursesMeta(System.currentTimeMillis() / 1000, it.size), it.toSortedMap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +206,18 @@ class CacheController {
|
||||||
logger.info("Updated courses successfully at ${Date(courseList.meta.updateTime * 1000)}")
|
logger.info("Updated courses successfully at ${Date(courseList.meta.updateTime * 1000)}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this function updates the roomList
|
||||||
|
* during the update process the old data will be returned for an API request
|
||||||
|
*/
|
||||||
|
private fun asyncUpdateRoomList() = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
RoomListParser().getLinks(StartupController.roomListURL)?.let {
|
||||||
|
roomList = RoomsList(RoomsMeta(System.currentTimeMillis() / 1000, it.size), it.toSortedMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Updated room list successfully at ${Date(courseList.meta.updateTime * 1000)}")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this function updates the mensa menu list
|
* this function updates the mensa menu list
|
||||||
* during the update process the old data will be returned for an API request
|
* during the update process the old data will be returned for an API request
|
||||||
|
@ -236,6 +285,52 @@ class CacheController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun asyncUpdateRoomSchedules() = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
logger.info("Updating ${roomScheduleList.size} room schedules ...")
|
||||||
|
|
||||||
|
// create a new ThreadPool with 5 threads
|
||||||
|
val executor = Executors.newFixedThreadPool(5)
|
||||||
|
|
||||||
|
try {
|
||||||
|
roomScheduleList.forEach { roomSchedule ->
|
||||||
|
executor.execute {
|
||||||
|
val roomScheduleParser = TimetableParser(roomSchedule.value.meta.link)
|
||||||
|
roomSchedule.value.timetable = roomScheduleParser.parseTimeTable() ?: return@execute
|
||||||
|
roomScheduleParser.parseCalendarWeek()?.also {
|
||||||
|
roomSchedule.value.meta.weekNumberYear = it.week
|
||||||
|
roomSchedule.value.meta.year = it.year
|
||||||
|
} ?: return@execute
|
||||||
|
roomSchedule.value.meta.updateTime = System.currentTimeMillis() / 1000
|
||||||
|
|
||||||
|
saveRoomScheduleToCache(roomSchedule.value) // save the updated timetable to the cache directory
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
logger.error("Error while updating the room schedules", ex)
|
||||||
|
} finally {
|
||||||
|
executor.shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save a timetable to the cache directory
|
||||||
|
* this is only call on async updates, it is NOT call when first getting the timetable
|
||||||
|
* @param roomSchedule a room schedule of the type [RoomScheduleWeekRet]
|
||||||
|
*/
|
||||||
|
private fun saveRoomScheduleToCache(roomSchedule: RoomScheduleWeekRet) {
|
||||||
|
val file = File(StartupController.dirTcorCache, "roomSchedule-${roomSchedule.meta.roomName}-${roomSchedule.meta.weekIndex}.json")
|
||||||
|
val writer = BufferedWriter(FileWriter(file))
|
||||||
|
|
||||||
|
try {
|
||||||
|
writer.write(Gson().toJson(roomSchedule))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("something went wrong while trying to write a cache file", e)
|
||||||
|
} finally {
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* before the APIController is up, get the data fist
|
* 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
|
* runBlocking: otherwise the api would return no data to requests for a few seconds after startup
|
||||||
|
@ -244,9 +339,11 @@ class CacheController {
|
||||||
// get all course links on startup, make sure there are course links
|
// get all course links on startup, make sure there are course links
|
||||||
val jobCourseUpdate = asyncUpdateCourseList()
|
val jobCourseUpdate = asyncUpdateCourseList()
|
||||||
val jobMensa = asyncUpdateMensa()
|
val jobMensa = asyncUpdateMensa()
|
||||||
|
val jobRoomListUpdate = asyncUpdateRoomList()
|
||||||
|
|
||||||
jobCourseUpdate.join()
|
jobCourseUpdate.join()
|
||||||
jobMensa.join()
|
jobMensa.join()
|
||||||
|
jobRoomListUpdate.join()
|
||||||
|
|
||||||
logger.info("Initial updates successful")
|
logger.info("Initial updates successful")
|
||||||
}
|
}
|
||||||
|
@ -269,15 +366,21 @@ class CacheController {
|
||||||
val initDelay3h = calcInitDelay(duration3h)
|
val initDelay3h = calcInitDelay(duration3h)
|
||||||
val initDelay1h = calcInitDelay(duration1h)
|
val initDelay1h = calcInitDelay(duration1h)
|
||||||
|
|
||||||
// update courseList every 24 hours (time in ms)
|
// update courseList and roomList every 24 hours (time in ms)
|
||||||
Timer().scheduleAtFixedRate(initDelay24h, duration24h) {
|
Timer().scheduleAtFixedRate(initDelay24h, duration24h) {
|
||||||
asyncUpdateCourseList()
|
asyncUpdateCourseList()
|
||||||
}
|
}
|
||||||
|
Timer().scheduleAtFixedRate(initDelay24h, duration24h) {
|
||||||
|
asyncUpdateRoomList()
|
||||||
|
}
|
||||||
|
|
||||||
// update all already existing timetables every 3 hours (time in ms)
|
// update all already existing timetables and room schedules every 3 hours (time in ms)
|
||||||
Timer().scheduleAtFixedRate(initDelay3h, duration3h) {
|
Timer().scheduleAtFixedRate(initDelay3h, duration3h) {
|
||||||
asyncUpdateTimetables()
|
asyncUpdateTimetables()
|
||||||
}
|
}
|
||||||
|
Timer().scheduleAtFixedRate(initDelay3h, duration3h) {
|
||||||
|
asyncUpdateRoomSchedules()
|
||||||
|
}
|
||||||
|
|
||||||
// update mensa menu every hour (time in ms)
|
// update mensa menu every hour (time in ms)
|
||||||
Timer().scheduleAtFixedRate(initDelay1h, duration1h) {
|
Timer().scheduleAtFixedRate(initDelay1h, duration1h) {
|
||||||
|
|
|
@ -44,6 +44,7 @@ class StartupController {
|
||||||
var cachetAPIKey = "0"
|
var cachetAPIKey = "0"
|
||||||
var cachetBaseURL = "https://status.mosad.xyz"
|
var cachetBaseURL = "https://status.mosad.xyz"
|
||||||
var courseListURL = "https://www.hs-offenburg.de/studium/vorlesungsplaene/"
|
var courseListURL = "https://www.hs-offenburg.de/studium/vorlesungsplaene/"
|
||||||
|
val roomListURL = "https://www.hs-offenburg.de/die-hochschule/organisation/infos-services/raumbelegungen"
|
||||||
var mensaMenuURL = "https://www.swfr.de/essen/mensen-cafes-speiseplaene/mensa-offenburg"
|
var mensaMenuURL = "https://www.swfr.de/essen/mensen-cafes-speiseplaene/mensa-offenburg"
|
||||||
var mensaName = "Offenburg"
|
var mensaName = "Offenburg"
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,10 @@ class StatusController {
|
||||||
private set
|
private set
|
||||||
var timetableRequests = HashMap<String, Int>()
|
var timetableRequests = HashMap<String, Int>()
|
||||||
private set
|
private set
|
||||||
|
var roomListRequests = 0
|
||||||
|
private set
|
||||||
|
var roomScheduleRequests = HashMap<String, Int>()
|
||||||
|
private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if a mensamenu/courseList/timetable is requested update the specific and total request count
|
* if a mensamenu/courseList/timetable is requested update the specific and total request count
|
||||||
|
@ -66,6 +70,16 @@ class StatusController {
|
||||||
totalRequests++
|
totalRequests++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateRoomListRequests() {
|
||||||
|
roomListRequests++
|
||||||
|
totalRequests++
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateRoomScheduleRequests(roomName: String) {
|
||||||
|
roomScheduleRequests[roomName] = (roomScheduleRequests[roomName] ?: 0) + 1
|
||||||
|
totalRequests++
|
||||||
|
}
|
||||||
|
|
||||||
fun getStatus(): Status {
|
fun getStatus(): Status {
|
||||||
val currentTime = System.currentTimeMillis() / 1000
|
val currentTime = System.currentTimeMillis() / 1000
|
||||||
val minutes = (currentTime - startTime) % 3600 / 60
|
val minutes = (currentTime - startTime) % 3600 / 60
|
||||||
|
@ -103,6 +117,10 @@ class StatusController {
|
||||||
timetableRequests,
|
timetableRequests,
|
||||||
CacheController.timetableList.size,
|
CacheController.timetableList.size,
|
||||||
Date(CacheController.courseList.meta.updateTime * 1000),
|
Date(CacheController.courseList.meta.updateTime * 1000),
|
||||||
|
roomListRequests,
|
||||||
|
roomScheduleRequests,
|
||||||
|
CacheController.roomScheduleList.size,
|
||||||
|
Date(CacheController.roomList.meta.updateTime * 1000),
|
||||||
Date(CacheController.mensaMenu.meta.updateTime * 1000),
|
Date(CacheController.mensaMenu.meta.updateTime * 1000),
|
||||||
hsoCode,
|
hsoCode,
|
||||||
swfrCode
|
swfrCode
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
/**
|
|
||||||
* TheCitadelofRicks
|
|
||||||
*
|
|
||||||
* Copyright 2019-2020 <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.hsoparser
|
|
||||||
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import org.mosad.thecitadelofricks.Course
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import java.net.SocketTimeoutException
|
|
||||||
|
|
||||||
class CourseListParser {
|
|
||||||
|
|
||||||
private var logger: org.slf4j.Logger = LoggerFactory.getLogger(CourseListParser::class.java)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return a list of all courses at courseListURL
|
|
||||||
* @param courseListURL the url to the course list page
|
|
||||||
* @return a ArrayList<Course> with all courses or null if the request was not successful
|
|
||||||
*/
|
|
||||||
fun getCourseLinks(courseListURL: String): HashMap<String, Course>? {
|
|
||||||
val courseLinkList = HashMap<String, Course>()
|
|
||||||
try {
|
|
||||||
val courseHTML = Jsoup.connect(courseListURL).get()
|
|
||||||
|
|
||||||
courseHTML.select("ul.index-group").select("li.Class").select("a[href]").forEachIndexed { _, element ->
|
|
||||||
courseLinkList[element.text()] = Course(
|
|
||||||
element.text(),
|
|
||||||
element.attr("href").replace("http:", "https:")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
logger.info("successfully retrieved course List")
|
|
||||||
} catch (ex: SocketTimeoutException) {
|
|
||||||
logger.warn("timeout from hs-offenburg.de, updating on next attempt!")
|
|
||||||
return null
|
|
||||||
} catch (gex: Exception) {
|
|
||||||
logger.error("general CourseListParser error", gex)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return courseLinkList
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/**
|
||||||
|
* TheCitadelofRicks
|
||||||
|
*
|
||||||
|
* Copyright 2019-2020 <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.hsoparser
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
import org.mosad.thecitadelofricks.Course
|
||||||
|
import org.mosad.thecitadelofricks.Room
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
|
|
||||||
|
sealed class TimetableLinkListParser<T> {
|
||||||
|
|
||||||
|
private var logger: org.slf4j.Logger = LoggerFactory.getLogger(TimetableLinkListParser::class.java)
|
||||||
|
|
||||||
|
abstract fun constructValue(key: String, link: String): T
|
||||||
|
|
||||||
|
abstract val blacklist: List<String>
|
||||||
|
|
||||||
|
abstract val liClass: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return a list of all elements at listURL
|
||||||
|
* @param listURL the url to the list page
|
||||||
|
* @return a ArrayList<T> with all links or null if the request was not successful
|
||||||
|
*/
|
||||||
|
fun getLinks(listURL: String): HashMap<String, T>? {
|
||||||
|
val linkList = HashMap<String, T>()
|
||||||
|
try {
|
||||||
|
val courseHTML = Jsoup.connect(listURL).get()
|
||||||
|
|
||||||
|
courseHTML
|
||||||
|
.select("ul.index-group")
|
||||||
|
.select("li.$liClass")
|
||||||
|
.select("a[href]")
|
||||||
|
.filter{ it: Element -> !blacklist.contains(it.text()) }
|
||||||
|
.forEach {
|
||||||
|
linkList[it.text()] = constructValue(
|
||||||
|
it.text(),
|
||||||
|
it.attr("href").replace("http:", "https:")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
logger.info("successfully retrieved link List")
|
||||||
|
} catch (ex: SocketTimeoutException) {
|
||||||
|
logger.warn("timeout from hs-offenburg.de, updating on next attempt!")
|
||||||
|
return null
|
||||||
|
} catch (gex: Exception) {
|
||||||
|
logger.error("general TimetableLinkListParser error", gex)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return linkList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CourseListParser : TimetableLinkListParser<Course>() {
|
||||||
|
override fun constructValue(key: String, link: String) = Course(key, link)
|
||||||
|
override val blacklist = emptyList<String>()
|
||||||
|
override val liClass = "Class"
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomListParser : TimetableLinkListParser<Room>() {
|
||||||
|
override fun constructValue(key: String, link: String) = Room(key, link)
|
||||||
|
override val blacklist = listOf("STÜBER SYSTEMS")
|
||||||
|
override val liClass = "Room"
|
||||||
|
}
|
Loading…
Reference in New Issue