Timetable fixes
- Only one request is made to get the timetable HTML document for parsing the timetable and the weekNumberYear - On timeouts or other errors, the cached data won't be overwritten with emptiness anymore - The scheduled updates will now also update the weekNumberYear
This commit is contained in:
parent
b4f7f48590
commit
cf979df46e
|
@ -58,7 +58,7 @@ data class TimetableDay(val timeslots: Array<ArrayList<Lesson>> = Array(6) { Arr
|
||||||
|
|
||||||
data class TimetableWeek(val days: Array<TimetableDay> = Array(6) { TimetableDay() })
|
data class TimetableWeek(val days: Array<TimetableDay> = Array(6) { TimetableDay() })
|
||||||
|
|
||||||
data class TimetableCourseMeta(var updateTime: Long = 0, val courseName: String = "", val weekIndex: Int = 0, val weekNumberYear: Int = 0, val link: String = "")
|
data class TimetableCourseMeta(var updateTime: Long = 0, val courseName: String = "", val weekIndex: Int = 0, var weekNumberYear: 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 = TimetableWeek())
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ class CacheController {
|
||||||
* @param weekIndex request week number (current week = 0)
|
* @param weekIndex request week number (current week = 0)
|
||||||
* @return timetable of the course (Type: [TimetableCourseWeek])
|
* @return timetable of the course (Type: [TimetableCourseWeek])
|
||||||
*/
|
*/
|
||||||
fun getTimetable(courseName: String, weekIndex: Int): TimetableCourseWeek = runBlocking {
|
fun getTimetable(courseName: String, weekIndex: Int): TimetableCourseWeek {
|
||||||
|
|
||||||
// TODO just for testing
|
// TODO just for testing
|
||||||
if (courseName == "TEST_A" || courseName == "TEST_B") {
|
if (courseName == "TEST_A" || courseName == "TEST_B") {
|
||||||
|
@ -68,27 +68,43 @@ class CacheController {
|
||||||
val weekNumberYear = 0
|
val weekNumberYear = 0
|
||||||
val instr = javaClass.getResourceAsStream("/html/Timetable_normal-week.html")
|
val instr = javaClass.getResourceAsStream("/html/Timetable_normal-week.html")
|
||||||
|
|
||||||
val htmlDoc = Jsoup.parse(instr,"UTF-8", "https://www.hs-offenburg.de/")
|
val timetableParser =
|
||||||
val timetableTest = TimetableParser().parseTimeTable(htmlDoc)
|
TimetableParser(htmlDoc = Jsoup.parse(instr, "UTF-8", "https://www.hs-offenburg.de/"))
|
||||||
|
val timetableTest = timetableParser.parseTimeTable()
|
||||||
|
|
||||||
return@runBlocking TimetableCourseWeek(TimetableCourseMeta(currentTime, courseName, weekIndex, weekNumberYear, timetableLink), timetableTest)
|
return TimetableCourseWeek(
|
||||||
|
TimetableCourseMeta(
|
||||||
|
currentTime,
|
||||||
|
courseName,
|
||||||
|
weekIndex,
|
||||||
|
weekNumberYear,
|
||||||
|
timetableLink
|
||||||
|
), timetableTest ?: TimetableWeek()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return@runBlocking timetableList.getOrPut("$courseName-$weekIndex") {
|
val key = "$courseName-$weekIndex"
|
||||||
|
return if (timetableList.contains(key)) {
|
||||||
|
timetableList[key]!!
|
||||||
|
} else {
|
||||||
val timetableLink = courseList.courses[courseName]
|
val timetableLink = courseList.courses[courseName]
|
||||||
?.courseLink
|
?.courseLink
|
||||||
?.replace("week=0", "week=$weekIndex") ?: ""
|
?.replace("week=0", "week=$weekIndex") ?: ""
|
||||||
val currentTime = System.currentTimeMillis() / 1000
|
val currentTime = System.currentTimeMillis() / 1000
|
||||||
var timetable = TimetableWeek()
|
|
||||||
var weekNumberYear = 0
|
|
||||||
|
|
||||||
val timetableJobs = listOf(
|
val timetableParser = TimetableParser(timetableLink)
|
||||||
async { timetable = TimetableParser().getTimeTable(timetableLink) },
|
val weekNumberYear = timetableParser.parseWeekNumberYear()
|
||||||
async { weekNumberYear = TimetableParser().getWeekNumberYear(timetableLink) }
|
val timetable = timetableParser.parseTimeTable()
|
||||||
)
|
|
||||||
|
|
||||||
timetableJobs.awaitAll()
|
TimetableCourseWeek(
|
||||||
TimetableCourseWeek(TimetableCourseMeta(currentTime, courseName, weekIndex, weekNumberYear, timetableLink), timetable)
|
TimetableCourseMeta(
|
||||||
|
currentTime,
|
||||||
|
courseName,
|
||||||
|
weekIndex,
|
||||||
|
weekNumberYear ?: 0,
|
||||||
|
timetableLink
|
||||||
|
), timetable ?: TimetableWeek()
|
||||||
|
).also { timetableList[key] = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +193,10 @@ class CacheController {
|
||||||
try {
|
try {
|
||||||
timetableList.forEach { timetableCourse ->
|
timetableList.forEach { timetableCourse ->
|
||||||
executor.execute {
|
executor.execute {
|
||||||
timetableCourse.value.timetable = TimetableParser().getTimeTable(timetableCourse.value.meta.link)
|
val timetableParser = TimetableParser(timetableCourse.value.meta.link)
|
||||||
|
timetableCourse.value.timetable = timetableParser.parseTimeTable() ?: return@execute
|
||||||
|
timetableCourse.value.meta.weekNumberYear =
|
||||||
|
timetableParser.parseWeekNumberYear() ?: return@execute
|
||||||
timetableCourse.value.meta.updateTime = System.currentTimeMillis() / 1000
|
timetableCourse.value.meta.updateTime = System.currentTimeMillis() / 1000
|
||||||
|
|
||||||
saveTimetableToCache(timetableCourse.value) // save the updated timetable to the cache directory
|
saveTimetableToCache(timetableCourse.value) // save the updated timetable to the cache directory
|
||||||
|
|
|
@ -28,26 +28,37 @@ import org.mosad.thecitadelofricks.Lesson
|
||||||
import org.mosad.thecitadelofricks.TimetableWeek
|
import org.mosad.thecitadelofricks.TimetableWeek
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
class TimetableParser {
|
/**
|
||||||
|
* @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)
|
||||||
|
*/
|
||||||
|
class TimetableParser(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")
|
||||||
|
|
||||||
|
private val htmlDoc: Document? =
|
||||||
|
htmlDoc
|
||||||
|
?: if (timetableURL == null) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Jsoup.connect(timetableURL).get()
|
||||||
|
} catch (gex: Exception) {
|
||||||
|
logger.error("general TimetableParser error", gex)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the timetable from the given url
|
* parse the timetable from the previously given url
|
||||||
* 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
|
||||||
* @param timetableURL the URL of the timetable you want to get
|
|
||||||
*/
|
*/
|
||||||
fun getTimeTable(timetableURL: String): TimetableWeek {
|
fun parseTimeTable(): TimetableWeek? {
|
||||||
return try {
|
if (htmlDoc == null) {
|
||||||
parseTimeTable(Jsoup.connect(timetableURL).get())
|
return null
|
||||||
} catch (gex: Exception) {
|
|
||||||
logger.error("general TimetableParser error", gex)
|
|
||||||
TimetableWeek()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun parseTimeTable(htmlDoc: Document): TimetableWeek {
|
|
||||||
val timetableWeek = TimetableWeek()
|
val timetableWeek = TimetableWeek()
|
||||||
val rows = htmlDoc.select("table.timetable").select("tr[scope=\"row\"]")
|
val rows = htmlDoc.select("table.timetable").select("tr[scope=\"row\"]")
|
||||||
|
|
||||||
|
@ -117,19 +128,13 @@ class TimetableParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the week number of the year for the timetable
|
* parse 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 {
|
fun parseWeekNumberYear(): Int? {
|
||||||
return try {
|
if (htmlDoc == null) {
|
||||||
parseWeekNumberYear(Jsoup.connect(timetableURL).get())
|
return null
|
||||||
} catch (gex: Exception) {
|
|
||||||
logger.error("general TimetableParser error", gex)
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun parseWeekNumberYear(htmlDoc: Document): Int {
|
|
||||||
return htmlDoc.select("h1.timetable-caption").text().substringAfter("- ")
|
return htmlDoc.select("h1.timetable-caption").text().substringAfter("- ")
|
||||||
.substringBefore(".").replace(" ", "").toInt()
|
.substringBefore(".").replace(" ", "").toInt()
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ class TimetableParserTest {
|
||||||
@Test
|
@Test
|
||||||
fun parseTimetableNormalWeek() {
|
fun parseTimetableNormalWeek() {
|
||||||
val htmlFile = File(TimetableParserTest::class.java.getResource("/html/Timetable_normal-week.html").path)
|
val htmlFile = File(TimetableParserTest::class.java.getResource("/html/Timetable_normal-week.html").path)
|
||||||
val htmlDoc = Jsoup.parse(htmlFile,"UTF-8", "https://www.hs-offenburg.de/")
|
val htmlDoc = Jsoup.parse(htmlFile, "UTF-8", "https://www.hs-offenburg.de/")
|
||||||
val actualTimetable = TimetableParser().parseTimeTable(htmlDoc).toString().trim()
|
val actualTimetable = TimetableParser(htmlDoc = htmlDoc).parseTimeTable().toString().trim()
|
||||||
val expectedTimetable = TimetableParserTest::class.java.getResource("/expected/Timetable_normal-week.txt").readText().trim()
|
val expectedTimetable = TimetableParserTest::class.java.getResource("/expected/Timetable_normal-week.txt").readText().trim()
|
||||||
|
|
||||||
Assertions.assertEquals(expectedTimetable, actualTimetable)
|
Assertions.assertEquals(expectedTimetable, actualTimetable)
|
||||||
|
@ -42,8 +42,8 @@ class TimetableParserTest {
|
||||||
@Test
|
@Test
|
||||||
fun parseTimetableEmptyWeek() {
|
fun parseTimetableEmptyWeek() {
|
||||||
val htmlFile = File(TimetableParserTest::class.java.getResource("/html/Timetable_empty-week.html").path)
|
val htmlFile = File(TimetableParserTest::class.java.getResource("/html/Timetable_empty-week.html").path)
|
||||||
val htmlDoc = Jsoup.parse(htmlFile,"UTF-8", "https://www.hs-offenburg.de/")
|
val htmlDoc = Jsoup.parse(htmlFile, "UTF-8", "https://www.hs-offenburg.de/")
|
||||||
val actualTimetable = TimetableParser().parseTimeTable(htmlDoc).toString().trim()
|
val actualTimetable = TimetableParser(htmlDoc = htmlDoc).parseTimeTable().toString().trim()
|
||||||
val expectedTimetable = TimetableParserTest::class.java.getResource("/expected/Timetable_empty-week.txt").readText().trim()
|
val expectedTimetable = TimetableParserTest::class.java.getResource("/expected/Timetable_empty-week.txt").readText().trim()
|
||||||
|
|
||||||
Assertions.assertEquals(expectedTimetable, actualTimetable)
|
Assertions.assertEquals(expectedTimetable, actualTimetable)
|
||||||
|
@ -52,8 +52,8 @@ class TimetableParserTest {
|
||||||
@Test
|
@Test
|
||||||
fun parseWeekNumberYear() {
|
fun parseWeekNumberYear() {
|
||||||
val htmlFile = File(TimetableParserTest::class.java.getResource("/html/Timetable_normal-week.html").path)
|
val htmlFile = File(TimetableParserTest::class.java.getResource("/html/Timetable_normal-week.html").path)
|
||||||
val htmlDoc = Jsoup.parse(htmlFile,"UTF-8", "https://www.hs-offenburg.de/")
|
val htmlDoc = Jsoup.parse(htmlFile, "UTF-8", "https://www.hs-offenburg.de/")
|
||||||
val actualWeekNumberYear = TimetableParser().parseWeekNumberYear(htmlDoc)
|
val actualWeekNumberYear = TimetableParser(htmlDoc = htmlDoc).parseWeekNumberYear()
|
||||||
|
|
||||||
Assertions.assertEquals(42, actualWeekNumberYear)
|
Assertions.assertEquals(42, actualWeekNumberYear)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue