TheCitadelofRicks/src/main/kotlin/org/mosad/thecitadelofricks/hsoparser/TimetableParser.kt

184 lines
6.8 KiB
Kotlin

/**
* 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.Document
import org.mosad.thecitadelofricks.Lesson
import org.mosad.thecitadelofricks.TimetableWeek
import org.slf4j.LoggerFactory
class TimetableParser {
private var logger: org.slf4j.Logger = LoggerFactory.getLogger(TimetableParser::class.java)
private val days = arrayOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
/**
* get the timetable from the given url
* 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
* @param timetableURL the URL of the timetable you want to get
*/
fun getTimeTable(timetableURL: String): TimetableWeek {
return try {
parseTimeTable(Jsoup.connect(timetableURL).get())
} catch (gex: Exception) {
logger.error("general TimetableParser error", gex)
TimetableWeek()
}
}
fun parseTimeTable(htmlDoc: Document): TimetableWeek {
val timetableWeek = TimetableWeek()
val rows = htmlDoc.select("table.timetable").select("tr[scope=\"row\"]")
var sDay = -1
var sRow = -1
var sLesson = Lesson("", "", "", "", "")
// get each row with index, reflects 1 timeslot per day
for ((rowIndex, row) in rows.withIndex()) {
var day = 0
var lessonIndexDay = 0 // the index of the lesson per timeslot
// elements are now all lessons, including empty ones
row.select("td.lastcol, td[style]").forEach {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))) {
// we found a lecture that is longer than 1 lesson
timetableWeek.days[day].timeslots[rowIndex].add(sLesson) // this just works if there is one lecture per slot
// adjust the following slot
sDay++
sLesson = Lesson(
"$day.$rowIndex.$lessonIndexDay",
element.select("div.lesson-subject").text(),
element.select("div.lesson-teacher").text(),
element.select("div.lesson-room").text(),
element.select("div.lesson-remark").text()
)
// adjust the slot directly as we don't get there anymore
if (sDay == 5) {
timetableWeek.days[day + 1].timeslots[rowIndex].add(sLesson)
}
} else {
timetableWeek.days[day].timeslots[rowIndex].add(
Lesson(
"$day.$rowIndex.$lessonIndexDay",
element.select("div.lesson-subject").text(),
element.select("div.lesson-teacher").text(),
element.select("div.lesson-room").text(),
element.select("div.lesson-remark").text()
)
)
}
// we found a lecture with rowspan="2", save day, row and lesson for later adjustment
if (element.toString().contains("rowspan=\"2\"")) {
sDay = day
sRow = rowIndex
sLesson = timetableWeek.days[day].timeslots[rowIndex].get(index = 0)
}
lessonIndexDay++
if (element.hasClass("lastcol"))
{
day++
lessonIndexDay = 0
}
}
}
return timetableWeek
}
/**
* get the week number of the year for the timetable
* @param timetableURL the URL of the timetable you want to get
*/
fun getWeekNumberYear(timetableURL: String): Int {
return try {
parseWeekNumberYear(Jsoup.connect(timetableURL).get())
} catch (gex: Exception) {
logger.error("general TimetableParser error", gex)
0
}
}
fun parseWeekNumberYear(htmlDoc: Document): Int {
return htmlDoc.select("h1.timetable-caption").text().substringAfter("- ")
.substringBefore(".").replace(" ", "").toInt()
}
@Suppress("unused")
/**
* print a timetable
* @param timetable the timetable to print
*/
fun printTimetableWeek(timetable: TimetableWeek) {
for (j in 0..5) print(days[j].padEnd(75, ' ') + " | ")
println()
for (j in 0..5) print("-".padEnd(76 + (j.toFloat().div(j).toInt()), '-') + "+")
println()
// the timeslot
for (i in 0..5) {
for (j in 0..5) {
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonSubject.padEnd(75 / ldiff, ' '))
if (ldiff == 2) print(" ")
print(" | ")
}
println()
for (j in 0..5) {
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonTeacher.padEnd(75 / ldiff, ' '))
if (ldiff == 2) print(" ")
print(" | ")
}
println()
for (j in 0..5) {
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonRoom.padEnd(75 / ldiff, ' '))
if (ldiff == 2) print(" ")
print(" | ")
}
println()
for (j in 0..5) print("-".padEnd(76 + (j.toFloat().div(j).toInt()), '-') + "+")
println()
}
println(" \n")
}
}