Successfully completed database change OMG; this will save memory by storing less duplicate data

This commit is contained in:
denizk0461
2023-04-17 14:19:06 +02:00
parent 4d520f375b
commit 315f2f815a
8 changed files with 137 additions and 57 deletions
@@ -1,7 +1,7 @@
package com.denizk0461.studip.data package com.denizk0461.studip.data
import android.util.Log import com.denizk0461.studip.db.EventRepository
import com.denizk0461.studip.model.CanteenOffer import com.denizk0461.studip.model.*
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.select.Elements import org.jsoup.select.Elements
@@ -9,10 +9,16 @@ import org.jsoup.select.Elements
// Parser class for the Studierendenwerk cafeteria plans // Parser class for the Studierendenwerk cafeteria plans
class StwParser { class StwParser {
private var id = 0 private var dateId = 0
private var canteenId = 0
private var categoryId = 0
private var itemId = 0
fun parse(onFinish: () -> Unit) { fun parse(onFinish: () -> Unit) {
id = 0 dateId = 0
canteenId = 0
categoryId = 0
itemId = 0
val links = mutableListOf<String>() val links = mutableListOf<String>()
if (true) { links.add(urlUniMensa) } if (true) { links.add(urlUniMensa) }
// if (true) { links.add(urlCafeCentral) } // if (true) { links.add(urlCafeCentral) }
@@ -27,27 +33,39 @@ class StwParser {
Dependencies.repo.nukeOffers() Dependencies.repo.nukeOffers()
// STEP: fetch every canteen
links.forEach { link -> links.forEach { link ->
Dependencies.repo.insertOffers(parseFromPage(link)) parseFromPage(link, Dependencies.repo)
} }
} }
private fun parseFromPage(url: String): List<CanteenOffer> { private fun parseFromPage(url: String, repo: EventRepository): List<CanteenOffer> {
val items = mutableListOf<CanteenOffer>() val items = mutableListOf<CanteenOffer>()
var date: String var date: String
var day = 0
dateId = 0
val doc = Jsoup.connect(url).get() val doc = Jsoup.connect(url).get()
repo.insert(OfferCanteen(canteenId, "TEMP")) // TODO canteen name
// STEP: get each day
doc.getElementsByClass("food-plan").forEach { dayPlan -> doc.getElementsByClass("food-plan").forEach { dayPlan ->
val rawDate = doc.getElementsByClass("tabs")[0] val rawDate = doc.getElementsByClass("tabs")[0]
.getElementsByClass("tab-date")[day].text().split(" ") .getElementsByClass("tab-date")[dateId].text().split(" ")
date = "${rawDate[0]}${rawDate[1].monthToNumber()}." date = "${rawDate[0]}${rawDate[1].monthToNumber()}."
day += 1
repo.insert(OfferDate(dateId, date))
// STEP: get each category in a day
dayPlan.getElementsByClass("food-category").forEach { category -> dayPlan.getElementsByClass("food-category").forEach { category ->
val categoryTitle = category.getElementsByClass("category-name")[0].text() val categoryTitle = category.getElementsByClass("category-name")[0].text()
repo.insert(OfferCategory(categoryId, dateId, canteenId, categoryTitle))
// STEP: get each item in a category
category category
.getElementsByTag("tbody")[0] .getElementsByTag("tbody")[0]
.getElementsByTag("tr").forEach { element -> .getElementsByTag("tr").forEach { element ->
@@ -56,11 +74,10 @@ class StwParser {
val prefs = val prefs =
element.getElementsByClass("field field-name-field-food-types")[0] element.getElementsByClass("field field-name-field-food-types")[0]
val newItem = CanteenOffer( repo.insert(
id = id, OfferItem(
date = date, itemId,
dateId = day, categoryId,
category = categoryTitle,
title = tableRows[1].getFilteredText(), title = tableRows[1].getFilteredText(),
price = tableRows.getTextOrEmpty(2), price = tableRows.getTextOrEmpty(2),
isFair = prefs.isDietaryPreferenceMet(PREFERENCE_FAIR), isFair = prefs.isDietaryPreferenceMet(PREFERENCE_FAIR),
@@ -74,11 +91,33 @@ class StwParser {
isVegetarian = prefs.isDietaryPreferenceMet(PREFERENCE_VEGETARIAN), isVegetarian = prefs.isDietaryPreferenceMet(PREFERENCE_VEGETARIAN),
isGame = prefs.isDietaryPreferenceMet(PREFERENCE_GAME), isGame = prefs.isDietaryPreferenceMet(PREFERENCE_GAME),
) )
items.add(newItem) )
id += 1 itemId += 1
// val newItem = CanteenOffer(
// id = id,
// date = date,
// dateId = day,
// category = categoryTitle,
// title = tableRows[1].getFilteredText(),
//// price = tableRows.getTextOrEmpty(2),
// isFair = prefs.isDietaryPreferenceMet(PREFERENCE_FAIR),
// isFish = prefs.isDietaryPreferenceMet(PREFERENCE_FISH),
// isPoultry = prefs.isDietaryPreferenceMet(PREFERENCE_POULTRY),
// isLamb = prefs.isDietaryPreferenceMet(PREFERENCE_LAMB),
// isVital = prefs.isDietaryPreferenceMet(PREFERENCE_VITAL),
// isBeef = prefs.isDietaryPreferenceMet(PREFERENCE_BEEF),
// isPork = prefs.isDietaryPreferenceMet(PREFERENCE_PORK),
// isVegan = prefs.isDietaryPreferenceMet(PREFERENCE_VEGAN),
// isVegetarian = prefs.isDietaryPreferenceMet(PREFERENCE_VEGETARIAN),
// isGame = prefs.isDietaryPreferenceMet(PREFERENCE_GAME),
// )
} }
categoryId += 1
} }
dateId += 1
} }
canteenId += 1
return items return items
} }
@@ -4,12 +4,12 @@ import androidx.lifecycle.LiveData
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import com.denizk0461.studip.model.CanteenOffer import com.denizk0461.studip.model.*
import com.denizk0461.studip.model.StudIPEvent
@Dao @Dao
interface EventDAO { interface EventDAO {
// Stud.IP schedule events
@get:Query("SELECT * FROM events ORDER BY id") @get:Query("SELECT * FROM events ORDER BY id")
val allEvents: LiveData<List<StudIPEvent>> val allEvents: LiveData<List<StudIPEvent>>
@@ -22,12 +22,39 @@ interface EventDAO {
@Query("DELETE FROM events") @Query("DELETE FROM events")
fun nukeEvents() fun nukeEvents()
@get:Query("SELECT * FROM offers ORDER BY id") // @get:Query("SELECT * FROM offers ORDER BY id")
// val allOffers: LiveData<List<CanteenOffer>>
//
// @Insert
// fun insertOffers(offers: List<CanteenOffer>)
//
// @Query("DELETE from offers")
// fun nukeOffers()
// canteen offers
// get all offers
@get:Query(
"SELECT * FROM offer_item " +
"JOIN offer_category ON offer_item.categoryId = offer_category.id " +
"JOIN offer_canteen ON offer_category.canteenId = offer_canteen.id " +
"JOIN offer_date ON offer_category.dateId = offer_date.id"
)
val allOffers: LiveData<List<CanteenOffer>> val allOffers: LiveData<List<CanteenOffer>>
@Insert @Query("DELETE FROM offer_date")
fun insertOffers(offers: List<CanteenOffer>) fun nukeOfferDates()
@Query("DELETE from offers") @Query("DELETE FROM offer_category")
fun nukeOffers() fun nukeOfferCategories()
@Query("DELETE FROM offer_canteen")
fun nukeOfferCanteens()
@Query("DELETE FROM offer_item")
fun nukeOfferItems()
@Insert fun insert(date: OfferDate)
@Insert fun insert(canteen: OfferCanteen)
@Insert fun insert(category: OfferCategory)
@Insert fun insert(item: OfferItem)
} }
@@ -4,10 +4,18 @@ import android.content.Context
import androidx.room.Database import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.denizk0461.studip.model.CanteenOffer import com.denizk0461.studip.model.*
import com.denizk0461.studip.model.StudIPEvent
@Database(entities = [StudIPEvent::class, CanteenOffer::class], version = 7) @Database(
entities = [
StudIPEvent::class,
OfferDate::class,
OfferCanteen::class,
OfferCategory::class,
OfferItem::class
],
version = 8
)
abstract class EventDatabase : RoomDatabase() { abstract class EventDatabase : RoomDatabase() {
abstract fun dao(): EventDAO abstract fun dao(): EventDAO
@@ -2,8 +2,7 @@ package com.denizk0461.studip.db
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.denizk0461.studip.model.CanteenOffer import com.denizk0461.studip.model.*
import com.denizk0461.studip.model.StudIPEvent
class EventRepository(app: Application) { class EventRepository(app: Application) {
@@ -17,6 +16,16 @@ class EventRepository(app: Application) {
val allOffers: LiveData<List<CanteenOffer>> = dao.allOffers val allOffers: LiveData<List<CanteenOffer>> = dao.allOffers
fun insertOffers(offers: List<CanteenOffer>) { dao.insertOffers(offers) } // fun insertOffers(offers: List<CanteenOffer>) { dao.insertOffers(offers) }
fun nukeOffers() { dao.nukeOffers() } fun nukeOffers() {
dao.nukeOfferItems()
dao.nukeOfferCategories()
dao.nukeOfferCanteens()
dao.nukeOfferDates()
}
fun insert(date: OfferDate) { dao.insert(date) }
fun insert(canteen: OfferCanteen) { dao.insert(canteen) }
fun insert(category: OfferCategory) { dao.insert(category) }
fun insert(item: OfferItem) { dao.insert(item) }
} }
@@ -1,16 +1,15 @@
package com.denizk0461.studip.model package com.denizk0461.studip.model
import androidx.room.Entity
import androidx.room.PrimaryKey
//@Entity(tableName = "offers") //@Entity(tableName = "offers")
data class CanteenOffer( data class CanteenOffer(
// @PrimaryKey val id: Int, // @PrimaryKey val id: Int,
val id: Int, val id: Int,
val date: String, val date: String,
val dateId: Int, // gives every item on the same day the same ID for easier filtering val dateId: Int,
// val canteen: Int,
val category: String, val category: String,
val categoryId: Int,
val canteen: String,
val canteenId: Int,
val title: String, val title: String,
val price: String, // price for students val price: String, // price for students
val isFair: Boolean, // artgerechte Tierhaltung val isFair: Boolean, // artgerechte Tierhaltung
@@ -1,6 +1,5 @@
package com.denizk0461.studip.model package com.denizk0461.studip.model
import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@@ -11,20 +10,20 @@ import androidx.room.PrimaryKey
ForeignKey( ForeignKey(
OfferDate::class, OfferDate::class,
["id"], ["id"],
["date_id"], ["dateId"],
ForeignKey.CASCADE, ForeignKey.CASCADE,
), ),
ForeignKey( ForeignKey(
OfferCanteen::class, OfferCanteen::class,
["id"], ["id"],
["canteen_id"], ["canteenId"],
ForeignKey.CASCADE, ForeignKey.CASCADE,
), ),
] ]
) )
data class OfferCategory( data class OfferCategory(
@PrimaryKey val id: Int, @PrimaryKey val id: Int,
@ColumnInfo(name = "date_id") val dateId: Int, val dateId: Int,
@ColumnInfo(name = "canteen_id") val canteenId: Int, val canteenId: Int,
val category: String, val category: String,
) )
@@ -1,6 +1,5 @@
package com.denizk0461.studip.model package com.denizk0461.studip.model
import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@@ -11,15 +10,15 @@ import androidx.room.PrimaryKey
ForeignKey( ForeignKey(
OfferCategory::class, OfferCategory::class,
["id"], ["id"],
["category_id"], ["categoryId"],
ForeignKey.CASCADE, ForeignKey.CASCADE,
), ),
] ]
) )
data class OfferItem( data class OfferItem(
@PrimaryKey val id: Int, @PrimaryKey val id: Int,
@ColumnInfo(name = "category_id") val categoryId: Int, val categoryId: Int,
val title: Int, val title: String,
val price: String, val price: String,
val isFair: Boolean, val isFair: Boolean,
val isFish: Boolean, val isFish: Boolean,
@@ -11,7 +11,7 @@ class CanteenViewModel(app: Application) : TemplateViewModel(app) {
val allOffers: LiveData<List<CanteenOffer>> = repo.allOffers val allOffers: LiveData<List<CanteenOffer>> = repo.allOffers
fun insertOffers(offers: List<CanteenOffer>) { doAsync { repo.insertOffers(offers) }} // fun insertOffers(offers: List<CanteenOffer>) { doAsync { repo.insertOffers(offers) }}
fun nukeOffers() { doAsync { repo.nukeOffers() }} fun nukeOffers() { doAsync { repo.nukeOffers() }}
fun fetchOffers(onFinish: () -> Unit) { doAsync { parser.parse(onFinish) }} fun fetchOffers(onFinish: () -> Unit) { doAsync { parser.parse(onFinish) }}