Archived
StudIPParser.kt now stops fetching items that don't provide full information, and tells the user how many items were skipped
This commit is contained in:
@@ -102,11 +102,17 @@ class FetcherActivity : FragmentActivity() {
|
|||||||
) { p0 ->
|
) { p0 ->
|
||||||
try {
|
try {
|
||||||
// Decode HTML and parse it into a list of StudIPEvent.kt
|
// Decode HTML and parse it into a list of StudIPEvent.kt
|
||||||
// TODO add a tutorial for the user to know what to do
|
val elementsNotFetched = viewModel.parse(URLDecoder.decode(p0, "UTF-8"))
|
||||||
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
|
// Close the activity
|
||||||
finish()
|
finish()
|
||||||
|
|||||||
@@ -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.
|
* Example: 13:20. (20 minutes) + (13 hours * 60 minutes) = 800 minutes.
|
||||||
*
|
*
|
||||||
* @return minute value of the string
|
* @return minute value of the string
|
||||||
*/
|
*/
|
||||||
fun String.parseToMinutes(): Int {
|
fun String.parseToMinutes(): Int = try {
|
||||||
val parts = split(":")
|
val parts = split(":")
|
||||||
return (parts[0].toInt() * 60) + parts[1].toInt()
|
(parts[0].toInt() * 60) + parts[1].toInt()
|
||||||
}
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a specified colour customised to the currently applied theme.
|
* Retrieves a specified colour customised to the currently applied theme.
|
||||||
|
|||||||
@@ -17,16 +17,20 @@ class StudIPParser(application: Application) {
|
|||||||
private val repo: AppRepository = AppRepository.getRepositoryInstance(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
|
* @param html website content of the Stud.IP timetable
|
||||||
* @throws IOException when an error in fetching or the database transaction occurs
|
* @throws IOException when an error in fetching or the database transaction occurs
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun parse(html: String) {
|
fun parse(html: String): Int {
|
||||||
// Primary key value to uniquely identify entries in the database
|
// Primary key value to uniquely identify entries in the database
|
||||||
var id = 0
|
var id = 0
|
||||||
|
|
||||||
|
// Counts how many elements could not be successfully fetched
|
||||||
|
var elementsNotFetched = 0
|
||||||
|
|
||||||
// Parse the HTML using Jsoup to traverse the document
|
// Parse the HTML using Jsoup to traverse the document
|
||||||
val doc = Jsoup.parse(html)
|
val doc = Jsoup.parse(html)
|
||||||
|
|
||||||
@@ -43,7 +47,7 @@ class StudIPParser(application: Application) {
|
|||||||
doc.getDateIndices().forEach { (index, element) ->
|
doc.getDateIndices().forEach { (index, element) ->
|
||||||
|
|
||||||
// Iterate through all events scheduled for a given day
|
// 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
|
* Retrieve the event's header. This will contain the event's title as well as the
|
||||||
* lecturers holding the event.
|
* lecturers holding the event.
|
||||||
@@ -57,10 +61,21 @@ class StudIPParser(application: Application) {
|
|||||||
val delimiter = entryHeader.lastIndexOf('(')
|
val delimiter = entryHeader.lastIndexOf('(')
|
||||||
|
|
||||||
// Retrieve the title of the event from the header string
|
// 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
|
// 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
|
* 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
|
// Retrieve the time slot and split it into start and end time stamps
|
||||||
val timeSlot = entryInfo[0].split(" - ")
|
val timeSlot = entryInfo[0].split(" - ")
|
||||||
|
|
||||||
// Time the event starts at
|
// Time the course starts at
|
||||||
val eventStart = timeSlot[0]
|
val eventStart = timeSlot.getOrQuestionMark(0)
|
||||||
|
|
||||||
// Construct the newly scraped Stud.IP event
|
// Construct the newly scraped Stud.IP event
|
||||||
val event = StudIPEvent(
|
val event = StudIPEvent(
|
||||||
eventId = id,
|
eventId = id,
|
||||||
title = parsedTitle,
|
title = parsedTitle,
|
||||||
lecturer = parsedLecturers,
|
lecturer = parsedLecturers,
|
||||||
room = entryInfo[1],
|
room = entryInfo.getOrQuestionMark(1),
|
||||||
day = index,
|
day = index,
|
||||||
timeslotStart = eventStart,
|
timeslotStart = eventStart,
|
||||||
timeslotEnd = timeSlot[1],
|
timeslotEnd = timeSlot.getOrQuestionMark(1),
|
||||||
timeslotId = eventStart.parseToMinutes(),
|
timeslotId = eventStart.parseToMinutes(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,6 +115,9 @@ class StudIPParser(application: Application) {
|
|||||||
|
|
||||||
// After fetching has finished, save the list of new items into persistent storage
|
// After fetching has finished, save the list of new items into persistent storage
|
||||||
repo.insertEvents(newEvents)
|
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
|
// Add the days found to the list
|
||||||
list.add(Pair(
|
list.add(Pair(
|
||||||
dayIndex,
|
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,
|
getElementById("calendar_view_1_column_${dayIndex}") ?: continue,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -148,4 +171,10 @@ class StudIPParser(application: Application) {
|
|||||||
// Return the list of columns
|
// Return the list of columns
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun List<String>.getOrQuestionMark(index: Int): String = try {
|
||||||
|
get(index)
|
||||||
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
"?"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -23,9 +23,9 @@ class FetcherViewModel(app: Application) : AppViewModel(app) {
|
|||||||
* @param html source code of the schedule website
|
* @param html source code of the schedule website
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun parse(html: String) {
|
fun parse(html: String): Int =
|
||||||
doAsync {
|
returnBlocking {
|
||||||
parser.parse(html)
|
parser.parse(html)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,11 @@
|
|||||||
<string name="fetch_error_webpage_snack">Du musst deinen Stundenplan öffnen!</string>
|
<string name="fetch_error_webpage_snack">Du musst deinen Stundenplan öffnen!</string>
|
||||||
<string name="canteen_fetch_error">Mensaplan konnte nicht heruntergeladen werden!</string>
|
<string name="canteen_fetch_error">Mensaplan konnte nicht heruntergeladen werden!</string>
|
||||||
|
|
||||||
|
<!-- plurals string resource cannot be used here because Android doesn't treat 0 differently from 2 -->
|
||||||
|
<string name="toast_fetch_finished_zero">Dein Stundenplan wurde aktualisiert!</string>
|
||||||
|
<string name="toast_fetch_finished_one">Dein Stundenplan wurde aktualisiert, doch eine Veranstaltung konnte nicht heruntergeladen werden.</string>
|
||||||
|
<string name="toast_fetch_finished_other">Dein Stundenplan wurde aktualisiert, doch %d Veranstaltungen konnten nicht heruntergeladen werden.</string>
|
||||||
|
|
||||||
<string name="fetch_bar_close_title">Schließen</string>
|
<string name="fetch_bar_close_title">Schließen</string>
|
||||||
<string name="fetch_bar_close_desc">Schließe die Webseite, ohne etwas zu speichern</string>
|
<string name="fetch_bar_close_desc">Schließe die Webseite, ohne etwas zu speichern</string>
|
||||||
<string name="fetch_bar_refresh_title">Aktualisieren</string>
|
<string name="fetch_bar_refresh_title">Aktualisieren</string>
|
||||||
@@ -134,8 +139,6 @@
|
|||||||
<string name="nav_food">Mensa</string>
|
<string name="nav_food">Mensa</string>
|
||||||
<string name="nav_settings">Einstellungen</string>
|
<string name="nav_settings">Einstellungen</string>
|
||||||
|
|
||||||
<string name="toast_fetch_finished">Dein Stundenplan wurde aktualisiert!</string>
|
|
||||||
|
|
||||||
<string name="settings_general">Allgemein</string>
|
<string name="settings_general">Allgemein</string>
|
||||||
<string name="settings_schedule">Stud.IP-Stundenplan</string>
|
<string name="settings_schedule">Stud.IP-Stundenplan</string>
|
||||||
<string name="settings_canteen">Mensenangebote</string>
|
<string name="settings_canteen">Mensenangebote</string>
|
||||||
|
|||||||
@@ -23,6 +23,11 @@
|
|||||||
<string name="fetch_error_webpage_snack">You must navigate to your schedule!</string>
|
<string name="fetch_error_webpage_snack">You must navigate to your schedule!</string>
|
||||||
<string name="canteen_fetch_error">Couldn\'t retrieve the canteen plan!</string>
|
<string name="canteen_fetch_error">Couldn\'t retrieve the canteen plan!</string>
|
||||||
|
|
||||||
|
<!-- plurals resource cannot be used here; view German translation for details -->
|
||||||
|
<string name="toast_fetch_finished_zero">Your schedule has been refreshed!</string>
|
||||||
|
<string name="toast_fetch_finished_one">Your schedule has been refreshed, but one item could not be saved.</string>
|
||||||
|
<string name="toast_fetch_finished_other">Your schedule has been refreshed, but %d items could not be saved.</string>
|
||||||
|
|
||||||
<string name="fetch_bar_close_title">Close</string>
|
<string name="fetch_bar_close_title">Close</string>
|
||||||
<string name="fetch_bar_close_desc">Close the website without saving anything</string>
|
<string name="fetch_bar_close_desc">Close the website without saving anything</string>
|
||||||
<string name="fetch_bar_refresh_title">Refresh</string>
|
<string name="fetch_bar_refresh_title">Refresh</string>
|
||||||
@@ -138,8 +143,6 @@
|
|||||||
<string name="nav_food">Canteens</string>
|
<string name="nav_food">Canteens</string>
|
||||||
<string name="nav_settings">Settings</string>
|
<string name="nav_settings">Settings</string>
|
||||||
|
|
||||||
<string name="toast_fetch_finished">Your schedule has been refreshed!</string>
|
|
||||||
|
|
||||||
<string name="settings_general">General</string>
|
<string name="settings_general">General</string>
|
||||||
<string name="settings_schedule">Stud.IP Schedule</string>
|
<string name="settings_schedule">Stud.IP Schedule</string>
|
||||||
<string name="settings_canteen">Canteen Offers</string>
|
<string name="settings_canteen">Canteen Offers</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user