diff --git a/app/src/main/java/com/denizk0461/weserplaner/data/StwParser.kt b/app/src/main/java/com/denizk0461/weserplaner/data/StwParser.kt
index 96e2728..4409646 100644
--- a/app/src/main/java/com/denizk0461/weserplaner/data/StwParser.kt
+++ b/app/src/main/java/com/denizk0461/weserplaner/data/StwParser.kt
@@ -6,6 +6,7 @@ import com.denizk0461.weserplaner.model.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jsoup.Jsoup
+import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
import kotlin.jvm.Throws
@@ -78,7 +79,9 @@ class StwParser(application: Application) {
try {
// Fetch offers from the chosen canteen
- val (dates, canteens, categories, items) = parseFromPage(link)
+ val (dates, canteens, categories, items) = parseFromPage(
+ "https://www.stw-bremen.de/de/$link"
+ )
// Delete all previous entries and start afresh
repo.nukeOffers()
@@ -129,41 +132,10 @@ class StwParser(application: Application) {
val doc = Jsoup.connect(url).get()
// Retrieve the opening hours for the canteen
- var openingHours = ""
+ val openingHours = doc.retrieveOpeningHours()
- // Iterate through all wrappers that could contain opening hours
- doc.getElementsByClass("details-wrapper").forEach {
- // Element is not for providing contact details
- if (it.getElementsByClass("contact-person-name").size == 0) {
-
- // Retrieve all opening hours.
- it.children().forEach { element ->
- if (element.tag().toString() == "p") {
- // Uni-Mensa is not parsed properly
- openingHours += element.textWithBreaks().trim() + "\n"
- } else {
- element.children().forEach { subElement ->
- openingHours += subElement.textWithBreaks() + '\n'
- }
- }
- }
- }
-
- // Add an extra line break to separate elements
- openingHours += "\n"
- }
-
- // Trim off excess line breaks
- openingHours = openingHours.trim()
-
- // Fetch news displayed at the top of the page, if any are available
- val news = try {
- doc.getElementsByClass("field__items")[0]
- .getElementsByTag("p")[0]
- .text()
- } catch (e: IndexOutOfBoundsException) {
- ""
- }
+ // Fetch news displayed at the top of the page
+ val news = doc.retrieveNews()
// Save the canteen to its list
canteens.add(
@@ -226,16 +198,16 @@ class StwParser(application: Application) {
.getElementsByClass("field field-name-field-food-types")[0]
) {
DietaryPreferences.Object(
- isFair = isDietaryPreferenceMet(imageLinkPrefFair),
- isFish = isDietaryPreferenceMet(imageLinkPrefFish),
- isPoultry = isDietaryPreferenceMet(imageLinkPrefPoultry),
- isLamb = isDietaryPreferenceMet(imageLinkPrefLamb),
- isVital = isDietaryPreferenceMet(imageLinkPrefVital),
- isBeef = isDietaryPreferenceMet(imageLinkPrefBeef),
- isPork = isDietaryPreferenceMet(imageLinkPrefPork),
- isVegan = isDietaryPreferenceMet(imageLinkPrefVegan),
- isVegetarian = isDietaryPreferenceMet(imageLinkPrefVegetarian),
- isGame = isDietaryPreferenceMet(imageLinkPrefGame),
+ isFair = isDietaryPreferenceMet(preferenceFairFileName),
+ isFish = isDietaryPreferenceMet(preferenceFishFileName),
+ isPoultry = isDietaryPreferenceMet(preferencePoultryFileName),
+ isLamb = isDietaryPreferenceMet(preferenceLambFileName),
+ isVital = isDietaryPreferenceMet(preferenceVitalFileName),
+ isBeef = isDietaryPreferenceMet(preferenceBeefFileName),
+ isPork = isDietaryPreferenceMet(preferencePorkFileName),
+ isVegan = isDietaryPreferenceMet(preferenceVeganFileName),
+ isVegetarian = isDietaryPreferenceMet(preferenceVegetarianFileName),
+ isGame = isDietaryPreferenceMet(preferenceGameFileName),
)
}
} catch (e: IndexOutOfBoundsException) {
@@ -310,11 +282,15 @@ class StwParser(application: Application) {
* Evaluates whether a dietary preference is met by checking if the link to an image can be
* found in the HTML.
*
+ * @receiver element to check the image source of
* @param preference constraint that needs to be met
* @return whether it is met
*/
private fun Element.isDietaryPreferenceMet(preference: String): Boolean =
- getElementsByAttributeValue("src", preference).isNotEmpty()
+ getElementsByAttributeValue(
+ "src",
+ "https://www.stw-bremen.de/sites/default/files/images/pictograms/$preference.png"
+ ).isNotEmpty()
/**
* Processes certain character references into human-readable characters. Since the fetched HTML
@@ -323,7 +299,8 @@ class StwParser(application: Application) {
* the website of the Studierendenwerk, but they are invisible, rendering them pointless to the
* website user.
*
- * @return the filtered string and a string describing allergens and additives
+ * @receiver element to parse the text content of
+ * @return the filtered string and a string describing allergens and additives
*/
private fun Element.getFilteredText(): Pair
tags with line breaks.
+ * Cleans up a HTML source by doing the following:
+ * - replace non-breaking spaces ' ' with a regular space character ' ',
+ * - replace closing paragraph tags '
", "") -// .replace("
", "") + private fun Element.cleanHtml(): String { + // Replace non-breaking spaces with a regular space character + var result: String = outerHtml().replace(" ", " ") + // Replace paragraph closing tags with a newline character + result = result.replace("", "\n") + + // Remove all other characters + return result.replace(Regex("?[a-zA-Z0-9]+>"), "") + } + + /** + * Retrieves the opening hours of a canteen from a given document. + * + * @receiver document to parse + * @return opening hours parsed as a string + */ + private fun Document.retrieveOpeningHours(): String { + // Retrieve the opening hours for the canteen + var openingHours = "" + + // Iterate through all containers that may contain opening hours + getElementsByClass("nm").forEach { container -> + // Attempt to fetch a header, if any is available + try { + // Check for a header in the parent element of the container + val header = container.parent()?.getElementsByClass("strong")?.get(0)?.text() + + // Check if the header has content for the opening hours + if (!header.isNullOrBlank() + && !header.contains("Frau") + && !header.contains("Herr") + ) { + // Add the header to the opening hours + openingHours += "${header}\n" + } + } catch (e: IndexOutOfBoundsException) { + // Ignore the exception; no header is available, and thus, nothing needs to be done + } + + // Iterate through all items in the container + container.children().forEach { element -> + // Clean up HTML + val line = element.cleanHtml() + + // Confirm that this line only contains information for the opening hours + if (!line.contains("Catering")) { + // Add the line to the opening hours + openingHours += "${line}\n" + } + } + } + + // Trim off excess line breaks + openingHours = openingHours.trim() + + // Return the result + return openingHours + } + + /** + * Retrieves news available at the top of the page in a special header. If none are available, + * return a blank string. + * + * @receiver document to parse + * @return news parsed as a string + */ + private fun Document.retrieveNews(): String = try { + getElementsByClass("field__items")[0] + .getElementsByTag("p")[0] + .text() + } catch (e: IndexOutOfBoundsException) { + "" + } + + /** + * Wrapper tuple class to transfer all fetched elements in one go. + * + * @param dates fetched date elements + * @param canteens fetched canteen elements + * @param categories fetched category elements + * @param items fetched items + */ private data class StwResults( val dates: List