Implemented allergen functionality by showing the user a bottom sheet

This commit is contained in:
denizk0461
2023-04-23 20:33:17 +02:00
parent b957172721
commit ca89e68a91
10 changed files with 251 additions and 14 deletions
@@ -156,14 +156,17 @@ class StwParser {
isGame = prefs.isDietaryPreferenceMet(imageLinkPrefGame), isGame = prefs.isDietaryPreferenceMet(imageLinkPrefGame),
).deconstruct() ).deconstruct()
val filteredText = tableRows[1].getFilteredText()
// Save the item to persistent storage // Save the item to persistent storage
repo.insert( repo.insert(
OfferItem( OfferItem(
itemId, itemId,
categoryId, categoryId,
title = tableRows[1].getFilteredText(), title = filteredText.first,
price = tableRows.getTextOrEmpty(2), price = tableRows.getTextOrEmpty(2),
prefString, dietaryPreferences = prefString,
allergens = filteredText.second,
) )
) )
@@ -193,28 +196,67 @@ class StwParser {
/** /**
* 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 &, they need to be replaced with their counterpart * contains unresolved symbols such as &, they need to be replaced with their counterpart
* (in this case, &). * (in this case, &). This method also parses allergens and additives, since they are listed on
* the website of the Studierendenwerk, but they are invisible, rendering them pointless to the
* website user.
* *
* @return the filtered string * @return the filtered string and a string describing allergens and additives
*/ */
private fun Element.getFilteredText(): String { private fun Element.getFilteredText(): 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;", "&")
.replace("&gt;", ">") .replace("&gt;", ">")
.replace("&lt;", "<") .replace("&lt;", "<")
// All allergens will be collected here
var allergens: String = ""
/*
* Used to determine whether any allergens have already been found in the item. Should this
* be the case, a delimiter is added.
*/
var hasAllergens: Boolean = false
/*
* Allergens are stored in super tags. Example:
* <sup>4, a1, a4, c, g</sup>
* This is the start index of the opening tag (inclusive).
*/
var indexSupOpen = 0
// This is the end index of the closing tag (exclusive)
var indexSupClose = 0
/* /*
* The Studierendenwerk's website lists allergens, but they are invisible, rendering them * The Studierendenwerk's website lists allergens, but they are invisible, rendering them
* pointless to the website user. As of now, this information is discarded in the app. * pointless to the website user. As of now, this information is discarded in the app.
* TODO implement allergen functionality * TODO implement allergen functionality
*/ */
// while (text.contains("<sup>")) { while (text.contains("<sup>")) {
// text = text.substring(0 until text.indexOf("<sup>")) + text.substring(text.indexOf("</sup>")+6 until text.length) // Add a delimiter if allergens have already been added
// } if (hasAllergens) allergens += ","
// Set indices for the <sup> tags
indexSupOpen = text.indexOf("<sup>")
indexSupClose = text.indexOf("</sup>")
// Add allergens
allergens += text.substring(indexSupOpen + 5 until indexSupClose)
// Remove allergens from the title of the offer
text = text.substring(0 until indexSupOpen) +
text.substring((indexSupClose + 6) until text.length)
/*
* Make sure that the delimiter will be added if more than one instance of allergens is
* found.
*/
hasAllergens = true
}
// Return the stripped and updated HTML string // Return the stripped and updated HTML string
return text return Pair(text, allergens)
} }
/** /**
@@ -17,7 +17,7 @@ import com.denizk0461.studip.model.*
OfferCategory::class, OfferCategory::class,
OfferItem::class OfferItem::class
], ],
version = 10, version = 11,
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
@@ -1,7 +1,6 @@
package com.denizk0461.studip.fragment package com.denizk0461.studip.fragment
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -130,6 +129,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
viewModel.fetchOffers(onRefreshUpdate = { status -> viewModel.fetchOffers(onRefreshUpdate = { status ->
// TODO refresh updates // TODO refresh updates
}, onFinish = { }, onFinish = {
createTabLayoutMediator()
// binding.swipeRefreshLayout.isRefreshing = false // binding.swipeRefreshLayout.isRefreshing = false
// createTabLayoutMediator() // createTabLayoutMediator()
}) })
@@ -138,16 +138,17 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
/** /**
* Create and attach the object mediating the tabs for the view pager. * Create and attach the object mediating the tabs for the view pager.
* TODO this is crash-prone
*/ */
private fun createTabLayoutMediator() { private fun createTabLayoutMediator() {
// Fetch all dates from the database // Fetch all dates from the database
val dates = viewModel.getDates() val dates = viewModel.getDates()
if (dates.isNotEmpty()) { if (dates.isNotEmpty()) {
Log.d("eek!7", "dates: ${dates}, viewpager pages: ${binding.viewPager.adapter?.itemCount}")
TabLayoutMediator(binding.dayTabLayout, binding.viewPager) { tab, position -> TabLayoutMediator(binding.dayTabLayout, binding.viewPager) { tab, position ->
Log.d("eek!9", position.toString())
tab.text = dates[position].date tab.text = dates[position].date
}.attach() }.attach()
} }
} }
@@ -264,6 +265,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
it.title, it.title,
it.price, it.price,
it.dietaryPreferences, it.dietaryPreferences,
it.allergens
) )
} }
) )
@@ -0,0 +1,58 @@
package com.denizk0461.studip.model
import com.denizk0461.studip.R
object Allergens {
/**
* Converts an allergen into the string ID for its localised full-text version. Elements are
* derived from the website of the Studierendenwerk Bremen:
* https://www.stw-bremen.de/de/mensa/allergene
*
* @param value allergen that will be converted
* @return string resource ID for the allergen
*/
fun getStringRes(value: String): Int = when (value) {
// Additives
"1" -> R.string.allergen_dye
"2" -> R.string.allergen_preservatives
"3" -> R.string.allergen_antioxidants
"4" -> R.string.allergen_phosphates
"5" -> R.string.allergen_sweeteners
"6" -> R.string.allergen_phenylalanine
"7" -> R.string.allergen_sulphured
"8" -> R.string.allergen_blackened
"9" -> R.string.allergen_waxed
"10" -> R.string.allergen_alcohol
"11" -> R.string.allergen_flavour_enhancers
"12" -> R.string.allergen_caffeine
"13" -> R.string.allergen_rennet
// Allergens
"a1" -> R.string.allergen_wheat
"a2" -> R.string.allergen_rye
"a3" -> R.string.allergen_barley
"a4" -> R.string.allergen_oat
"a5" -> R.string.allergen_spelt
"a6" -> R.string.allergen_kamut
"b" -> R.string.allergen_crustaceans
"c" -> R.string.allergen_eggs
"d" -> R.string.allergen_fish
"e" -> R.string.allergen_peanuts
"f" -> R.string.allergen_soy
"g" -> R.string.allergen_dairy
"h1" -> R.string.allergen_almonds
"h2" -> R.string.allergen_hazelnuts
"h3" -> R.string.allergen_walnuts
"h4" -> R.string.allergen_cashew_nuts
"h5" -> R.string.allergen_pecans
"h6" -> R.string.allergen_brazil_nuts
"h7" -> R.string.allergen_pistachios
"h8" -> R.string.allergen_macadamia
"i" -> R.string.allergen_celery
"j" -> R.string.allergen_mustard
"k" -> R.string.allergen_sulphides
"l" -> R.string.allergen_lupins
"m" -> R.string.allergen_sesame
"n" -> R.string.allergen_molluscs
else -> 0 // shouldn't occur
}
}
@@ -19,6 +19,7 @@ import androidx.room.ColumnInfo
* @param price students' price for the individual canteen offer * @param price students' price for the individual canteen offer
* @param dietaryPreferences dietary preferences used to filter for the user's needs - see * @param dietaryPreferences dietary preferences used to filter for the user's needs - see
* DietaryPrefObject.kt * DietaryPrefObject.kt
* @param allergens allergens and additives that are present in the item
*/ */
data class CanteenOffer( data class CanteenOffer(
val itemId: Int, val itemId: Int,
@@ -31,4 +32,5 @@ data class CanteenOffer(
val title: String, val title: String,
val price: String, val price: String,
@ColumnInfo(name = "dietary_preferences") val dietaryPreferences: String, @ColumnInfo(name = "dietary_preferences") val dietaryPreferences: String,
val allergens: String,
) )
@@ -9,9 +9,11 @@ package com.denizk0461.studip.model
* @param price students' price for the individual canteen offer * @param price students' price for the individual canteen offer
* @param dietaryPreferences dietary preferences used to filter for the user's needs - see * @param dietaryPreferences dietary preferences used to filter for the user's needs - see
* DietaryPrefObject.kt * DietaryPrefObject.kt
* @param allergens allergens and additives that are present in the item
*/ */
data class CanteenOfferGroupElement( data class CanteenOfferGroupElement(
val title: String, val title: String,
val price: String, val price: String,
val dietaryPreferences: String, val dietaryPreferences: String,
val allergens: String,
) )
@@ -14,6 +14,7 @@ import androidx.room.PrimaryKey
* @param price students' price for the individual canteen offer * @param price students' price for the individual canteen offer
* @param dietaryPreferences dietary preferences used to filter for the user's needs - see * @param dietaryPreferences dietary preferences used to filter for the user's needs - see
* DietaryPrefObject.kt * DietaryPrefObject.kt
* @param allergens allergens and additives that are present in the item
*/ */
@Entity( @Entity(
tableName = "offer_item", tableName = "offer_item",
@@ -32,4 +33,5 @@ data class OfferItem(
val title: String, val title: String,
val price: String, val price: String,
@ColumnInfo(name = "dietary_preferences") val dietaryPreferences: String, @ColumnInfo(name = "dietary_preferences") val dietaryPreferences: String,
val allergens: String,
) )
@@ -5,6 +5,7 @@ import android.view.View
import com.denizk0461.studip.R import com.denizk0461.studip.R
import com.denizk0461.studip.data.viewBinding import com.denizk0461.studip.data.viewBinding
import com.denizk0461.studip.databinding.SheetAllergenBinding import com.denizk0461.studip.databinding.SheetAllergenBinding
import com.denizk0461.studip.model.Allergens
import com.denizk0461.studip.model.CanteenOfferGroupElement import com.denizk0461.studip.model.CanteenOfferGroupElement
class AllergenSheet(private val offer: CanteenOfferGroupElement) : AppSheet(R.layout.sheet_allergen) { class AllergenSheet(private val offer: CanteenOfferGroupElement) : AppSheet(R.layout.sheet_allergen) {
@@ -16,7 +17,43 @@ class AllergenSheet(private val offer: CanteenOfferGroupElement) : AppSheet(R.la
binding.apply { binding.apply {
title.text = offer.title title.text = offer.title
textContent.text = "hello there" textContent.text = offer.allergens.compileAllergenString()
} }
} }
/**
* Retrieves localised strings for all allergens present in a given offer and compiles them into
* a human-readable string, if any allergens are present.
*
* @return a string of all allergens
*/
private fun String.compileAllergenString(): String {
// Let the user know if no allergens are present
if (this.isBlank()) return getString(R.string.allergens_none)
// Stores all localised allergens
var allergenString = ""
// Used to insert a delimiter if more than one allergen is found
var isFirst = true
// Split all allergens to iterate through them
val allergens = this.replace(" ", "").split(",")
// Iterate through all allergens
allergens.forEach { allergen ->
// Insert a delimiter if more than one allergen is found
if (!isFirst) allergenString += ", "
// Add a localised string for the allergen
allergenString += getString(Allergens.getStringRes(allergen))
// Set when the first allergen has been processed
isFirst = false
}
// Insert the allergens into a template string
return getString(R.string.allergens_contains, allergenString)
}
} }
+46
View File
@@ -34,6 +34,52 @@
<string name="pref_vegetarian">Vegetarisch</string> <string name="pref_vegetarian">Vegetarisch</string>
<string name="pref_game">Wild</string> <string name="pref_game">Wild</string>
<!-- Zusatzstoffe -->
<string name="allergen_dye">Farbstoffe</string>
<string name="allergen_preservatives">Konservierungsstoffe</string>
<string name="allergen_antioxidants">Antioxidationsmittel</string>
<string name="allergen_phosphates">Phosphate</string>
<string name="allergen_sweeteners">Süßungsmittel</string>
<string name="allergen_phenylalanine">Phenylalaninquelle</string>
<string name="allergen_blackened">geschwärzt</string>
<string name="allergen_sulphured">geschwefelt</string>
<string name="allergen_waxed">gewachst</string>
<string name="allergen_alcohol">Alkohol</string>
<string name="allergen_flavour_enhancers">Geschmacksverstärker</string>
<string name="allergen_caffeine">Koffein</string>
<string name="allergen_rennet">tierisches Lab</string>
<!-- Allergene -->
<string name="allergen_wheat">Weizen</string>
<string name="allergen_rye">Roggen</string>
<string name="allergen_barley">Gerste</string>
<string name="allergen_oat">Hafer</string>
<string name="allergen_spelt">Dinkel</string>
<string name="allergen_kamut">Kamut</string>
<string name="allergen_crustaceans">Krebstiere</string>
<string name="allergen_eggs">Eier</string>
<string name="allergen_fish">Fisch</string>
<string name="allergen_peanuts">Erdnüsse</string>
<string name="allergen_soy">Soja</string>
<string name="allergen_dairy">Milch &amp; Laktose</string>
<string name="allergen_almonds">Mandeln</string>
<string name="allergen_hazelnuts">Haselnüsse</string>
<string name="allergen_walnuts">Walnüsse</string>
<string name="allergen_cashew_nuts">Cashew-Kerne</string>
<string name="allergen_pecans">Pecannüsse</string>
<string name="allergen_brazil_nuts">Paranüsse</string>
<string name="allergen_pistachios">Pistazien</string>
<string name="allergen_macadamia">Macadamianüsse</string>
<string name="allergen_celery">Sellerie</string>
<string name="allergen_mustard">Senf</string>
<string name="allergen_sulphides">Schwefeldioxide &amp; Sulfide von mehr als 10mg/kg or 10mg/l</string>
<string name="allergen_lupins">Lupinen</string>
<string name="allergen_sesame">Sesam</string>
<string name="allergen_molluscs">Weichtiere</string>
<string name="allergens_none">Enthält weder Allergene noch Zusatzstoffe.</string>
<string name="allergens_contains">Enthält: %s.</string>
<string name="nav_schedule">Stundenplan</string> <string name="nav_schedule">Stundenplan</string>
<string name="nav_food">Mensen</string> <string name="nav_food">Mensen</string>
<string name="nav_settings">Einstellungen</string> <string name="nav_settings">Einstellungen</string>
+46
View File
@@ -34,6 +34,52 @@
<string name="pref_vegetarian">Vegetarian</string> <string name="pref_vegetarian">Vegetarian</string>
<string name="pref_game">Game</string> <string name="pref_game">Game</string>
<!-- additives -->
<string name="allergen_dye">dye</string>
<string name="allergen_preservatives">preservatives</string>
<string name="allergen_antioxidants">antioxidants</string>
<string name="allergen_phosphates">phosphates</string>
<string name="allergen_sweeteners">sweeteners</string>
<string name="allergen_phenylalanine">a source of phenylalanine</string>
<string name="allergen_blackened">blackened</string>
<string name="allergen_sulphured">sulphured</string>
<string name="allergen_waxed">waxed</string>
<string name="allergen_alcohol">alcohol</string>
<string name="allergen_flavour_enhancers">flavour enhancers</string>
<string name="allergen_caffeine">caffeine</string>
<string name="allergen_rennet">animal rennet</string>
<!-- allergens -->
<string name="allergen_wheat">wheat</string>
<string name="allergen_rye">rye</string>
<string name="allergen_barley">barley</string>
<string name="allergen_oat">oats</string>
<string name="allergen_spelt">spelt</string>
<string name="allergen_kamut">kamut</string>
<string name="allergen_crustaceans">crustaceans</string>
<string name="allergen_eggs">eggs</string>
<string name="allergen_fish">fish</string>
<string name="allergen_peanuts">peanuts</string>
<string name="allergen_soy">soy</string>
<string name="allergen_dairy">dairy &amp; lactose</string>
<string name="allergen_almonds">almonds</string>
<string name="allergen_hazelnuts">hazelnuts</string>
<string name="allergen_walnuts">walnuts</string>
<string name="allergen_cashew_nuts">cashew nuts</string>
<string name="allergen_pecans">pecans</string>
<string name="allergen_brazil_nuts">brazil nuts</string>
<string name="allergen_pistachios">pistachios</string>
<string name="allergen_macadamia">macadamia nuts</string>
<string name="allergen_celery">celery</string>
<string name="allergen_mustard">mustard</string>
<string name="allergen_sulphides">sulphur oxide &amp; sulphide concentrated above 10mg/kg or 10mg/l</string>
<string name="allergen_lupins">lupins</string>
<string name="allergen_sesame">sesame</string>
<string name="allergen_molluscs">molluscs</string>
<string name="allergens_none">Contains no allergens or additives.</string>
<string name="allergens_contains">Contains: %s.</string>
<string name="nav_schedule">Schedule</string> <string name="nav_schedule">Schedule</string>
<string name="nav_food">Canteens</string> <string name="nav_food">Canteens</string>
<string name="nav_settings">Settings</string> <string name="nav_settings">Settings</string>