/** * TheCitadelofRicks * * Copyright 2019-2020 * * 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") } }