Merge pull request 'Parse course name for room occupancies' (#30) from parse-lessoncourse into master

Reviewed-on: #30
This commit is contained in:
Jannik 2023-09-29 23:12:06 +02:00
commit 302e1c7307
4 changed files with 52 additions and 26 deletions

View File

@ -102,7 +102,7 @@ class APIController {
@RequestParam(value = "course", defaultValue = "AI4") courseName: String, @RequestParam(value = "course", defaultValue = "AI4") courseName: String,
@RequestParam(value = "subject", defaultValue = "Mathematik 4") lessonSubject: String, @RequestParam(value = "subject", defaultValue = "Mathematik 4") lessonSubject: String,
@RequestParam(value = "week", defaultValue = "0") week: Int @RequestParam(value = "week", defaultValue = "0") week: Int
): ArrayList<Lesson> { ): ArrayList<LessonWithRoom> {
logger.info("lesson request at ${LocalDateTime.now()}!") logger.info("lesson request at ${LocalDateTime.now()}!")
updateTimetableRequests(courseName) updateTimetableRequests(courseName)
return getLesson(courseName, lessonSubject, week) return getLesson(courseName, lessonSubject, week)

View File

@ -49,7 +49,7 @@ data class MensaMenu(val meta: MensaMeta, val currentWeek: MensaWeek, val nextWe
data class CalendarWeek(val week: Int, val year: Int) data class CalendarWeek(val week: Int, val year: Int)
data class Lesson( data class LessonWithRoom(
val lessonID: String, val lessonID: String,
val lessonSubject: String, val lessonSubject: String,
val lessonTeacher: String, val lessonTeacher: String,
@ -57,13 +57,21 @@ data class Lesson(
val lessonRemark: String val lessonRemark: String
) )
data class TimetableDay(val timeslots: Array<ArrayList<Lesson>> = Array(6) { ArrayList<Lesson>() }) data class LessonWithCourse(
val lessonID: String,
val lessonSubject: String,
val lessonTeacher: String,
val lessonCourse: String,
val lessonRemark: String
)
data class TimetableWeek(val days: Array<TimetableDay> = Array(6) { TimetableDay() }) data class TimetableDay<Lesson>(val timeslots: Array<ArrayList<Lesson>> = Array(6) { ArrayList<Lesson>() })
data class TimetableWeek<Lesson>(val days: Array<TimetableDay<Lesson>> = Array(6) { TimetableDay() })
data class TimetableCourseMeta(var updateTime: Long = 0, val courseName: String = "", val weekIndex: Int = 0, var weekNumberYear: Int = 0, var year: Int = 0, val link: String = "") data class TimetableCourseMeta(var updateTime: Long = 0, val courseName: String = "", val weekIndex: Int = 0, var weekNumberYear: Int = 0, var year: Int = 0, val link: String = "")
data class TimetableCourseWeek(val meta: TimetableCourseMeta = TimetableCourseMeta(), var timetable: TimetableWeek = TimetableWeek()) data class TimetableCourseWeek(val meta: TimetableCourseMeta = TimetableCourseMeta(), var timetable: TimetableWeek<LessonWithRoom> = TimetableWeek())
// data classes for the room occupancy part // data classes for the room occupancy part
data class Room(val roomName: String, val roomLink: String) data class Room(val roomName: String, val roomLink: String)
@ -75,7 +83,7 @@ data class RoomsListRet(val meta: RoomsMeta = RoomsMeta(), val rooms: 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 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 class RoomScheduleWeekRet(val meta: RoomScheduleMeta = RoomScheduleMeta(), var timetable: TimetableWeek<LessonWithCourse> = TimetableWeek())
// data classes for the status part // data classes for the status part

View File

@ -27,9 +27,10 @@ import kotlinx.coroutines.*
import org.jsoup.Jsoup 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.CourseTimetableParser
import org.mosad.thecitadelofricks.hsoparser.MensaParser import org.mosad.thecitadelofricks.hsoparser.MensaParser
import org.mosad.thecitadelofricks.hsoparser.RoomListParser import org.mosad.thecitadelofricks.hsoparser.RoomListParser
import org.mosad.thecitadelofricks.hsoparser.TimetableParser import org.mosad.thecitadelofricks.hsoparser.RoomTimetableParser
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.* import java.io.*
@ -41,7 +42,6 @@ import kotlin.collections.HashSet
import kotlin.concurrent.scheduleAtFixedRate import kotlin.concurrent.scheduleAtFixedRate
import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
import kotlin.time.ExperimentalTime
class CacheController { class CacheController {
@ -76,7 +76,7 @@ class CacheController {
val instr = CacheController::class.java.getResourceAsStream("/html/Timetable_normal-week.html") val instr = CacheController::class.java.getResourceAsStream("/html/Timetable_normal-week.html")
val timetableParser = val timetableParser =
TimetableParser(htmlDoc = Jsoup.parse(instr!!, "UTF-8", "https://www.hs-offenburg.de/")) CourseTimetableParser(htmlDoc = Jsoup.parse(instr!!, "UTF-8", "https://www.hs-offenburg.de/"))
val timetableTest = timetableParser.parseTimeTable() val timetableTest = timetableParser.parseTimeTable()
return TimetableCourseWeek( return TimetableCourseWeek(
@ -100,7 +100,7 @@ class CacheController {
?.replace("week=0", "week=$weekIndex") ?: "" ?.replace("week=0", "week=$weekIndex") ?: ""
val currentTime = System.currentTimeMillis() / 1000 val currentTime = System.currentTimeMillis() / 1000
val timetableParser = TimetableParser(timetableLink) val timetableParser = CourseTimetableParser(timetableLink)
val calendarWeek = timetableParser.parseCalendarWeek() val calendarWeek = timetableParser.parseCalendarWeek()
val timetable = timetableParser.parseTimeTable() val timetable = timetableParser.parseTimeTable()
@ -142,8 +142,8 @@ class CacheController {
* @param weekIndex request week number (current week = 0) * @param weekIndex request week number (current week = 0)
* @return a ArrayList<[Lesson]> of every lesson with lessonSubject for one week * @return a ArrayList<[Lesson]> of every lesson with lessonSubject for one week
*/ */
fun getLesson(courseName: String, lessonSubject: String, weekIndex: Int): ArrayList<Lesson> { fun getLesson(courseName: String, lessonSubject: String, weekIndex: Int): ArrayList<LessonWithRoom> {
val lessonList = ArrayList<Lesson>() val lessonList = ArrayList<LessonWithRoom>()
// get all lessons from the weeks timetable // get all lessons from the weeks timetable
val flatMap = getTimetable(courseName, weekIndex).timetable.days.flatMap { it.timeslots.asIterable() } val flatMap = getTimetable(courseName, weekIndex).timetable.days.flatMap { it.timeslots.asIterable() }
@ -171,7 +171,7 @@ class CacheController {
?.replace("week=0", "week=$weekIndex") ?: "" ?.replace("week=0", "week=$weekIndex") ?: ""
val currentTime = System.currentTimeMillis() / 1000 val currentTime = System.currentTimeMillis() / 1000
val roomScheduleParser = TimetableParser(roomScheduleLink) val roomScheduleParser = RoomTimetableParser(roomScheduleLink)
val calendarWeek = roomScheduleParser.parseCalendarWeek() val calendarWeek = roomScheduleParser.parseCalendarWeek()
val roomSchedule = roomScheduleParser.parseTimeTable() val roomSchedule = roomScheduleParser.parseTimeTable()
@ -248,7 +248,7 @@ class CacheController {
try { try {
timetableList.forEach { timetableCourse -> timetableList.forEach { timetableCourse ->
executor.execute { executor.execute {
val timetableParser = TimetableParser(timetableCourse.value.meta.link) val timetableParser = CourseTimetableParser(timetableCourse.value.meta.link)
timetableCourse.value.timetable = timetableParser.parseTimeTable() ?: return@execute timetableCourse.value.timetable = timetableParser.parseTimeTable() ?: return@execute
timetableParser.parseCalendarWeek()?.also { timetableParser.parseCalendarWeek()?.also {
timetableCourse.value.meta.weekNumberYear = it.week timetableCourse.value.meta.weekNumberYear = it.week
@ -294,7 +294,7 @@ class CacheController {
try { try {
roomScheduleList.forEach { roomSchedule -> roomScheduleList.forEach { roomSchedule ->
executor.execute { executor.execute {
val roomScheduleParser = TimetableParser(roomSchedule.value.meta.link) val roomScheduleParser = RoomTimetableParser(roomSchedule.value.meta.link)
roomSchedule.value.timetable = roomScheduleParser.parseTimeTable() ?: return@execute roomSchedule.value.timetable = roomScheduleParser.parseTimeTable() ?: return@execute
roomScheduleParser.parseCalendarWeek()?.also { roomScheduleParser.parseCalendarWeek()?.also {
roomSchedule.value.meta.weekNumberYear = it.week roomSchedule.value.meta.weekNumberYear = it.week

View File

@ -27,7 +27,8 @@ import kotlinx.coroutines.sync.Semaphore
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.mosad.thecitadelofricks.CalendarWeek import org.mosad.thecitadelofricks.CalendarWeek
import org.mosad.thecitadelofricks.Lesson import org.mosad.thecitadelofricks.LessonWithCourse
import org.mosad.thecitadelofricks.LessonWithRoom
import org.mosad.thecitadelofricks.TimetableWeek import org.mosad.thecitadelofricks.TimetableWeek
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -35,10 +36,13 @@ import org.slf4j.LoggerFactory
* @param timetableURL the URL of the timetable you want to get * @param timetableURL the URL of the timetable you want to get
* @param htmlDoc the html document to use (the timetableURL will be ignored if this value is present) * @param htmlDoc the html document to use (the timetableURL will be ignored if this value is present)
*/ */
class TimetableParser(timetableURL: String? = null, htmlDoc: Document? = null) { sealed class TimetableParser<Lesson>(timetableURL: String? = null, htmlDoc: Document? = null) {
private var logger: org.slf4j.Logger = LoggerFactory.getLogger(TimetableParser::class.java) private var logger: org.slf4j.Logger = LoggerFactory.getLogger(TimetableParser::class.java)
private val days = arrayOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") private val days = arrayOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
abstract fun constructLesson(lessonID: String, lessonSubject: String, lessonTeacher: String, value4: String, lessonRemark: String): Lesson
abstract val value4Class: String
companion object { companion object {
val semaphore = Semaphore(3, 0) val semaphore = Semaphore(3, 0)
} }
@ -63,13 +67,13 @@ class TimetableParser(timetableURL: String? = null, htmlDoc: Document? = null) {
* the timetable is organised per row not per column; * the timetable is organised per row not per column;
* Mon 1, Tue 1, Wed 1, Thur 1, Fri 1, Sat 1, Mon 2 and so on * Mon 1, Tue 1, Wed 1, Thur 1, Fri 1, Sat 1, Mon 2 and so on
*/ */
fun parseTimeTable(): TimetableWeek? = htmlDoc?.let { fun parseTimeTable(): TimetableWeek<Lesson>? = htmlDoc?.let {
val timetableWeek = TimetableWeek() val timetableWeek = TimetableWeek<Lesson>()
val rows = it.select("table.timetable").select("tr[scope=\"row\"]") val rows = it.select("table.timetable").select("tr[scope=\"row\"]")
var sDay = -1 var sDay = -1
var sRow = -1 var sRow = -1
var sLesson = Lesson("", "", "", "", "") var sLesson = constructLesson("", "", "", "", "")
// get each row with index, reflects 1 timeslot per day // get each row with index, reflects 1 timeslot per day
for ((rowIndex, row) in rows.withIndex()) { for ((rowIndex, row) in rows.withIndex()) {
@ -86,11 +90,11 @@ class TimetableParser(timetableURL: String? = null, htmlDoc: Document? = null) {
// adjust the following slot // adjust the following slot
sDay++ sDay++
sLesson = Lesson( sLesson = constructLesson(
"$day.$rowIndex.$lessonIndexDay", "$day.$rowIndex.$lessonIndexDay",
element.select("div.lesson-subject").text(), element.select("div.lesson-subject").text(),
element.select("div.lesson-teacher").text(), element.select("div.lesson-teacher").text(),
element.select("div.lesson-room").text(), element.select("div.$value4Class").text(),
element.select("div.lesson-remark").text() element.select("div.lesson-remark").text()
) )
@ -101,11 +105,11 @@ class TimetableParser(timetableURL: String? = null, htmlDoc: Document? = null) {
} else { } else {
timetableWeek.days[day].timeslots[rowIndex].add( timetableWeek.days[day].timeslots[rowIndex].add(
Lesson( constructLesson(
"$day.$rowIndex.$lessonIndexDay", "$day.$rowIndex.$lessonIndexDay",
element.select("div.lesson-subject").text(), element.select("div.lesson-subject").text(),
element.select("div.lesson-teacher").text(), element.select("div.lesson-teacher").text(),
element.select("div.lesson-room").text(), element.select("div.$value4Class").text(),
element.select("div.lesson-remark").text() element.select("div.lesson-remark").text()
) )
) )
@ -146,7 +150,7 @@ class TimetableParser(timetableURL: String? = null, htmlDoc: Document? = null) {
* print a timetable * print a timetable
* @param timetable the timetable to print * @param timetable the timetable to print
*/ */
fun printTimetableWeek(timetable: TimetableWeek) { fun printTimetableWeek(timetable: TimetableWeek<LessonWithRoom>) {
for (j in 0..5) print(days[j].padEnd(75, ' ') + " | ") for (j in 0..5) print(days[j].padEnd(75, ' ') + " | ")
println() println()
for (j in 0..5) print("-".padEnd(76 + (j.toFloat().div(j).toInt()), '-') + "+") for (j in 0..5) print("-".padEnd(76 + (j.toFloat().div(j).toInt()), '-') + "+")
@ -189,4 +193,18 @@ class TimetableParser(timetableURL: String? = null, htmlDoc: Document? = null) {
println(" \n") println(" \n")
} }
} }
class CourseTimetableParser(timetableURL: String? = null, htmlDoc: Document? = null) : TimetableParser<LessonWithRoom>(timetableURL, htmlDoc) {
override fun constructLesson(lessonID: String, lessonSubject: String, lessonTeacher: String, value4: String, lessonRemark: String)
= LessonWithRoom(lessonID, lessonSubject, lessonTeacher, value4, lessonRemark)
override val value4Class = "lesson-room"
}
class RoomTimetableParser(timetableURL: String? = null, htmlDoc: Document? = null) : TimetableParser<LessonWithCourse>(timetableURL, htmlDoc) {
override fun constructLesson(lessonID: String, lessonSubject: String, lessonTeacher: String, value4: String, lessonRemark: String)
= LessonWithCourse(lessonID, lessonSubject, lessonTeacher, value4, lessonRemark)
override val value4Class = "lesson-class"
}