diff --git a/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt b/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt index cd089a4..84e2eb1 100644 --- a/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt +++ b/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt @@ -102,11 +102,17 @@ class FetcherActivity : FragmentActivity() { ) { p0 -> try { // Decode HTML and parse it into a list of StudIPEvent.kt - // TODO add a tutorial for the user to know what to do - viewModel.parse(URLDecoder.decode(p0, "UTF-8")) + val elementsNotFetched = viewModel.parse(URLDecoder.decode(p0, "UTF-8")) - // Notify the user that the fetch was successful - showToast(this, getString(R.string.toast_fetch_finished)) + /* + * Notify the user that the fetch was successful, and tell the user how many + * items could not be fetched. + */ + showToast(this, when (elementsNotFetched) { + 0 -> getString(R.string.toast_fetch_finished_zero) + 1 -> getString(R.string.toast_fetch_finished_one) + else -> getString(R.string.toast_fetch_finished_other, elementsNotFetched) + }) // Close the activity finish() diff --git a/app/src/main/java/com/denizk0461/studip/data/Extensions.kt b/app/src/main/java/com/denizk0461/studip/data/Extensions.kt index d6ea87d..ac6875e 100644 --- a/app/src/main/java/com/denizk0461/studip/data/Extensions.kt +++ b/app/src/main/java/com/denizk0461/studip/data/Extensions.kt @@ -23,15 +23,17 @@ import kotlin.jvm.Throws */ /** - * Converts a time stamp string to its numeric value in minutes. + * Converts a time stamp string to its numeric value in minutes. Returns 0 on error. * Example: 13:20. (20 minutes) + (13 hours * 60 minutes) = 800 minutes. * * @return minute value of the string */ -fun String.parseToMinutes(): Int { - val parts = split(":") - return (parts[0].toInt() * 60) + parts[1].toInt() -} +fun String.parseToMinutes(): Int = try { + val parts = split(":") + (parts[0].toInt() * 60) + parts[1].toInt() + } catch (e: IndexOutOfBoundsException) { + 0 + } /** * Retrieves a specified colour customised to the currently applied theme. diff --git a/app/src/main/java/com/denizk0461/studip/data/StudIPParser.kt b/app/src/main/java/com/denizk0461/studip/data/StudIPParser.kt index 569799f..2ee0807 100644 --- a/app/src/main/java/com/denizk0461/studip/data/StudIPParser.kt +++ b/app/src/main/java/com/denizk0461/studip/data/StudIPParser.kt @@ -17,16 +17,20 @@ class StudIPParser(application: Application) { private val repo: AppRepository = AppRepository.getRepositoryInstance(application) /** - * Parse a given HTML string. HTML must be of a Stud.IP timetable. + * Parse a given HTML string. HTML must be of a Stud.IP timetable. May skip elements that don't + * provide full information on an event (title, timeslot, lecturers, room). * * @param html website content of the Stud.IP timetable * @throws IOException when an error in fetching or the database transaction occurs */ @Throws(IOException::class) - fun parse(html: String) { + fun parse(html: String): Int { // Primary key value to uniquely identify entries in the database var id = 0 + // Counts how many elements could not be successfully fetched + var elementsNotFetched = 0 + // Parse the HTML using Jsoup to traverse the document val doc = Jsoup.parse(html) @@ -43,7 +47,7 @@ class StudIPParser(application: Application) { doc.getDateIndices().forEach { (index, element) -> // Iterate through all events scheduled for a given day - element.getElementsByClass("schedule_entry").forEach { entry -> + element.getElementsByClass("schedule_entry").forEach loop@{ entry -> /* * Retrieve the event's header. This will contain the event's title as well as the * lecturers holding the event. @@ -57,10 +61,21 @@ class StudIPParser(application: Application) { val delimiter = entryHeader.lastIndexOf('(') // Retrieve the title of the event from the header string - val parsedTitle = entryHeader.substring(0 until delimiter).trim() + val parsedTitle = try { + entryHeader.substring(0 until delimiter).trim() + } catch (e: StringIndexOutOfBoundsException) { + elementsNotFetched += 1 + return@loop + } // Retrieve the lecturer name(s) from the header string - val parsedLecturers = entryHeader.substring(delimiter + 1 until entryHeader.length - 1).trim() + val parsedLecturers = try { + entryHeader + .substring(delimiter + 1 until entryHeader.length - 1).trim() + } catch (e: StringIndexOutOfBoundsException) { + elementsNotFetched += 1 + return@loop + } /* * Retrieve further event information. This will contain both the time slot the @@ -72,18 +87,18 @@ class StudIPParser(application: Application) { // Retrieve the time slot and split it into start and end time stamps val timeSlot = entryInfo[0].split(" - ") - // Time the event starts at - val eventStart = timeSlot[0] + // Time the course starts at + val eventStart = timeSlot.getOrQuestionMark(0) // Construct the newly scraped Stud.IP event val event = StudIPEvent( eventId = id, title = parsedTitle, lecturer = parsedLecturers, - room = entryInfo[1], + room = entryInfo.getOrQuestionMark(1), day = index, timeslotStart = eventStart, - timeslotEnd = timeSlot[1], + timeslotEnd = timeSlot.getOrQuestionMark(1), timeslotId = eventStart.parseToMinutes(), ) @@ -100,6 +115,9 @@ class StudIPParser(application: Application) { // After fetching has finished, save the list of new items into persistent storage repo.insertEvents(newEvents) + + // Return the count of elements that were not able to be fetched + return elementsNotFetched } /** @@ -141,6 +159,11 @@ class StudIPParser(application: Application) { // Add the days found to the list list.add(Pair( dayIndex, + /* + * The index of the columns doesn't seem to depend on the other elements in the + * table. Friday will always have the index 4 ("calendar_view_1_column_4") even if + * Monday through Thursday are hidden from the schedule. + */ getElementById("calendar_view_1_column_${dayIndex}") ?: continue, )) } @@ -148,4 +171,10 @@ class StudIPParser(application: Application) { // Return the list of columns return list } + + private fun List.getOrQuestionMark(index: Int): String = try { + get(index) + } catch (e: IndexOutOfBoundsException) { + "?" + } } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/viewmodel/FetcherViewModel.kt b/app/src/main/java/com/denizk0461/studip/viewmodel/FetcherViewModel.kt index 340801f..be7dc2a 100644 --- a/app/src/main/java/com/denizk0461/studip/viewmodel/FetcherViewModel.kt +++ b/app/src/main/java/com/denizk0461/studip/viewmodel/FetcherViewModel.kt @@ -23,9 +23,9 @@ class FetcherViewModel(app: Application) : AppViewModel(app) { * @param html source code of the schedule website */ @Throws(IOException::class) - fun parse(html: String) { - doAsync { + fun parse(html: String): Int = + returnBlocking { parser.parse(html) } - } + } \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a2f077a..2afe04a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -23,6 +23,11 @@ Du musst deinen Stundenplan öffnen! Mensaplan konnte nicht heruntergeladen werden! + + Dein Stundenplan wurde aktualisiert! + Dein Stundenplan wurde aktualisiert, doch eine Veranstaltung konnte nicht heruntergeladen werden. + Dein Stundenplan wurde aktualisiert, doch %d Veranstaltungen konnten nicht heruntergeladen werden. + Schließen Schließe die Webseite, ohne etwas zu speichern Aktualisieren @@ -134,8 +139,6 @@ Mensa Einstellungen - Dein Stundenplan wurde aktualisiert! - Allgemein Stud.IP-Stundenplan Mensenangebote diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 73cea8a..d0a2325 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,6 +23,11 @@ You must navigate to your schedule! Couldn\'t retrieve the canteen plan! + + Your schedule has been refreshed! + Your schedule has been refreshed, but one item could not be saved. + Your schedule has been refreshed, but %d items could not be saved. + Close Close the website without saving anything Refresh @@ -138,8 +143,6 @@ Canteens Settings - Your schedule has been refreshed! - General Stud.IP Schedule Canteen Offers