Flattened structure of StwParser.kt somewhat; CanteenFragment.kt now shows an error snack if the user tries to switch canteens while another one is being fetched; made slight adjustments to the translations

This commit is contained in:
denizk0461
2023-05-14 09:00:08 +02:00
parent 2311cc46ad
commit d9a4ead7ec
4 changed files with 124 additions and 86 deletions
@@ -98,6 +98,7 @@ class StwParser(application: Application) {
onFinish() onFinish()
} catch (e: RuntimeException) { } catch (e: RuntimeException) {
// e.printStackTrace()
/* /*
* Call action to let the user know an error occurred. Do this on the main thread to * Call action to let the user know an error occurred. Do this on the main thread to
* access UI. * access UI.
@@ -122,9 +123,6 @@ class StwParser(application: Application) {
val categories = mutableListOf<OfferCategory>() val categories = mutableListOf<OfferCategory>()
val items = mutableListOf<OfferItem>() val items = mutableListOf<OfferItem>()
// Used to store a parsed date string without creating a new variable on every loop
var date: String
// Reset the primary key value for the date objects // Reset the primary key value for the date objects
dateId = 0 dateId = 0
@@ -150,30 +148,16 @@ class StwParser(application: Application) {
// Iterate through each day for which offers are available // Iterate through each day for which offers are available
doc.getElementsByClass("food-plan").forEach { dayPlan -> doc.getElementsByClass("food-plan").forEach { dayPlan ->
/* // Get and add the date of this food plan
* Fetch the date for a given day. It will be in the following format: dates.add(doc.getDateByIndex(dateId))
* 24. Apr
* This value will be split between the day (24.) and the month (Apr)
*/
val rawDate = doc.getElementsByClass("tabs")[0]
.getElementsByClass("tab-date")[dateId].text().split(" ")
/*
* Parse the two elements into a single date. The month will be converted to a numeric
* value. Example:
* Input: 24. Apr
* Output: 24.04.
*/
date = "${rawDate[0]}${rawDate[1].monthToNumber()}."
// Save the date to its list
dates.add(OfferDate(dateId, date))
// Set that the newly created date has no items as of yet
dateHasItems = false dateHasItems = false
// Iterate through all categories offered on a given day // Iterate through all categories offered on a given day
dayPlan.getElementsByClass("food-category").forEach { category -> dayPlan.getElementsByClass("food-category").forEach { category ->
// Set that items for the newly created date have been found
dateHasItems = true dateHasItems = true
// Retrieve the category text // Retrieve the category text
@@ -183,67 +167,46 @@ class StwParser(application: Application) {
categories.add(OfferCategory(categoryId, dateId, canteenId, categoryTitle)) categories.add(OfferCategory(categoryId, dateId, canteenId, categoryTitle))
// Iterate through all items in a category // Iterate through all items in a category
category category.getAllFoodItems().forEach { element ->
.getElementsByTag("tbody")[0] // Retrieve the parent element holding the item's title and price
.getElementsByTag("tr").forEach { element -> val tableRows = element.getElementsByTag("td")
// Retrieve the parent element holding the item's title and price
val tableRows = element.getElementsByTag("td")
/* // Retrieve the dietary preferences the item meets
* Retrieve the dietary preferences the item meets and parse them into a val prefs = getOrElse(DietaryPreferences.NONE_MET) {
* string that can be inserted into the database. element
*/ .getElementsByClass("field field-name-field-food-types")[0]
val prefs = try { .getDietaryPreferencesObject()
with(element
.getElementsByClass("field field-name-field-food-types")[0]
) {
DietaryPreferences.Object(
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) {
DietaryPreferences.NONE_MET
}
val filteredText = getOrElse(orElse = Pair("", "")) {
tableRows[1].getFilteredText()
}
// Save the item to its list
items.add(
OfferItem(
itemId,
categoryId,
title = filteredText.first,
price = tableRows.getTextOrEmpty(
/*
* ID stored for student: 0
* Index required to fetch student price: 2
*
* ID stored for employee: 1
* Index required to fetch employee price: 3
*
* Hence, getIntPreference() + 2
*/
repo.getIntPreference(SettingsPreferences.PRICING) + 2
),
dietaryPreferences = prefs.deconstruct(),
allergens = filteredText.second,
)
)
// Increment the item ID to avoid conflict
itemId += 1
} }
// Retrieve title and embedded allergens individually
val filteredText = getOrElse(orElse = Pair("", "")) {
tableRows[1].splitTitleAndAllergens()
}
// Save the item to its list
items.add(OfferItem(
itemId,
categoryId,
title = filteredText.first,
price = tableRows.getTextOrEmpty(
/*
* ID stored for student: 0
* Index required to fetch student price: 2
*
* ID stored for employee: 1
* Index required to fetch employee price: 3
*
* Hence, getIntPreference() + 2
*/
repo.getIntPreference(SettingsPreferences.PRICING) + 2
),
dietaryPreferences = prefs.deconstruct(),
allergens = filteredText.second,
))
// Increment the item ID to avoid conflict
itemId += 1
}
// Increment the category ID to avoid conflict // Increment the category ID to avoid conflict
categoryId += 1 categoryId += 1
} }
@@ -278,6 +241,44 @@ class StwParser(application: Application) {
return StwResults(dates, canteens, categories, items) return StwResults(dates, canteens, categories, items)
} }
/**
* Retrieves a date object from the canteen website by its index.
*
* @receiver document to parse
* @param index position to retrieve date from
* @return date object
*/
private fun Document.getDateByIndex(index: Int): OfferDate {
/*
* Fetch the date for a given day. It will be in the following format:
* 24. Apr
* This value will be split between the day (24.) and the month (Apr)
*/
val rawDate = getElementsByClass("tabs")[0]
.getElementsByClass("tab-date")[index].text().split(" ")
/*
* Parse the two elements into a single date. The month will be converted to a numeric
* value. Example:
* Input: 24. Apr
* Output: 24.04.
*/
val date = "${rawDate[0]}${rawDate[1].monthToNumber()}."
// Save the date to its list
return OfferDate(dateId, date)
}
/**
* Retrieves all food items from a given element for a given day.
*
* @receiver element to retrieve food item elements from
* @return list of food elements
*/
private fun Element.getAllFoodItems(): Elements =
getElementsByTag("tbody")[0].getElementsByTag("tr")
/** /**
* Evaluates whether a dietary preference is met by checking if the link to an image can be * Evaluates whether a dietary preference is met by checking if the link to an image can be
* found in the HTML. * found in the HTML.
@@ -293,6 +294,27 @@ class StwParser(application: Application) {
).isNotEmpty() ).isNotEmpty()
/** /**
* Retrieves a dietary preferences object from a given Element.
*
* @receiver element to retrieve preferences from
* @return [DietaryPreferences.Object]
*/
private fun Element.getDietaryPreferencesObject(): DietaryPreferences.Object =
DietaryPreferences.Object(
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),
)
/**
* Retrieves the item's title and its dietary preferences that are embedded in its title.
* Processes certain character references into human-readable characters. Since the fetched HTML * Processes certain character references into human-readable characters. Since the fetched HTML
* contains unresolved symbols such as &amp;, they need to be replaced with their counterpart * contains unresolved symbols such as &amp;, they need to be replaced with their counterpart
* (in this case, &). This method also parses allergens and additives, since they are listed on * (in this case, &). This method also parses allergens and additives, since they are listed on
@@ -300,9 +322,9 @@ class StwParser(application: Application) {
* website user. * website user.
* *
* @receiver element to parse the text content of * @receiver element to parse the text content of
* @return the filtered string and a string describing allergens and additives * @return the filtered title and a string describing allergens and additives
*/ */
private fun Element.getFilteredText(): Pair<String, String> { private fun Element.splitTitleAndAllergens(): Pair<String, String> {
// Retrieve the element's inner HTML and replace faulty characters // Retrieve the element's inner HTML and replace faulty characters
var text = html() var text = html()
.replace("&amp;", "&") .replace("&amp;", "&")
@@ -153,8 +153,22 @@ class CanteenFragment : AppFragment() {
} }
} }
// Set up floating action button for switching the canteen // Set up floating action button for switching canteens
binding.fabSwitchCanteen.setOnClickListener { binding.fabSwitchCanteen.setOnClickListener {
/*
* Show an error message is the user is trying to switch canteens while another one is
* being downloaded.
*/
if (binding.swipeRefreshLayout.isRefreshing) {
context.theme.showErrorSnackBar(
binding.snackbarContainer,
getString(R.string.canteen_fab_switch_canteen_error),
)
return@setOnClickListener
}
// Create menu for selecting a new canteen
PopupMenu(binding.root.context, binding.fabSwitchCanteen).apply { PopupMenu(binding.root.context, binding.fabSwitchCanteen).apply {
setOnMenuItemClickListener { item -> setOnMenuItemClickListener { item ->
viewModel.preferenceCanteen = when (item?.itemId) { viewModel.preferenceCanteen = when (item?.itemId) {
+4 -3
View File
@@ -52,7 +52,7 @@
<string name="sheet_schedule_update_header_edit">Bestehende Veranstaltung bearbeiten</string> <string name="sheet_schedule_update_header_edit">Bestehende Veranstaltung bearbeiten</string>
<string name="sheet_schedule_update_header_add">Neue Veranstaltung hinzufügen</string> <string name="sheet_schedule_update_header_add">Neue Veranstaltung hinzufügen</string>
<string name="sheet_schedule_update_title_hint">Veranstaltungstitel</string> <string name="sheet_schedule_update_title_hint">Veranstaltungstitel</string>
<string name="sheet_schedule_update_lecturers_hint">Dozierende(r)</string> <string name="sheet_schedule_update_lecturers_hint">Dozierende*r</string>
<string name="sheet_schedule_update_room_hint">Raum</string> <string name="sheet_schedule_update_room_hint">Raum</string>
<string name="sheet_schedule_update_hint_quarter">Auf akademisches Viertel umstellen</string> <string name="sheet_schedule_update_hint_quarter">Auf akademisches Viertel umstellen</string>
<string name="sheet_schedule_update_hint_quarter_error">Sorry, ein Fehler ist aufgetreten!</string> <string name="sheet_schedule_update_hint_quarter_error">Sorry, ein Fehler ist aufgetreten!</string>
@@ -68,7 +68,8 @@
<string name="canteen_no_offer_desc">Für diesen Tag sind keine Angebote vorhanden</string> <string name="canteen_no_offer_desc">Für diesen Tag sind keine Angebote vorhanden</string>
<string name="canteen_news_sheet_title">Neuigkeiten für diese Mensa</string> <string name="canteen_news_sheet_title">Neuigkeiten für diese Mensa</string>
<string name="canteen_fab_opening_hours_content_desc">Zeige Öffnungszeiten</string> <string name="canteen_fab_opening_hours_content_desc">Zeige Öffnungszeiten</string>
<string name="canteen_fab_switch_canteen">Wechsle Mensa</string> <string name="canteen_fab_switch_canteen">Mensa wechseln</string>
<string name="canteen_fab_switch_canteen_error">Ein Plan wird gerade heruntergeladen.</string>
<string name="canteen_fab_refresh_content_desc">Aktualisiere Angebote</string> <string name="canteen_fab_refresh_content_desc">Aktualisiere Angebote</string>
<string name="canteen_fab_refresh_text">Aktualisieren</string> <string name="canteen_fab_refresh_text">Aktualisieren</string>
@@ -145,7 +146,7 @@
<string name="allergen_almonds">Mandeln</string> <string name="allergen_almonds">Mandeln</string>
<string name="allergen_hazelnuts">Haselnüsse</string> <string name="allergen_hazelnuts">Haselnüsse</string>
<string name="allergen_walnuts">Walnüsse</string> <string name="allergen_walnuts">Walnüsse</string>
<string name="allergen_cashew_nuts">Cashew-Kerne</string> <string name="allergen_cashew_nuts">Cashewkerne</string>
<string name="allergen_pecans">Pecannüsse</string> <string name="allergen_pecans">Pecannüsse</string>
<string name="allergen_brazil_nuts">Paranüsse</string> <string name="allergen_brazil_nuts">Paranüsse</string>
<string name="allergen_pistachios">Pistazien</string> <string name="allergen_pistachios">Pistazien</string>
+1
View File
@@ -71,6 +71,7 @@
<string name="canteen_news_sheet_title">News for this canteen</string> <string name="canteen_news_sheet_title">News for this canteen</string>
<string name="canteen_fab_opening_hours_content_desc">Show opening hours</string> <string name="canteen_fab_opening_hours_content_desc">Show opening hours</string>
<string name="canteen_fab_switch_canteen">Switch canteen</string> <string name="canteen_fab_switch_canteen">Switch canteen</string>
<string name="canteen_fab_switch_canteen_error">A plan is being downloaded.</string>
<string name="canteen_fab_refresh_content_desc">Refresh offers</string> <string name="canteen_fab_refresh_content_desc">Refresh offers</string>
<string name="canteen_fab_refresh_text">Refresh</string> <string name="canteen_fab_refresh_text">Refresh</string>