From a72accada6d6b5836bf77b8a6b63cf5676643b49 Mon Sep 17 00:00:00 2001 From: denizk0461 Date: Fri, 12 Jan 2024 17:19:58 +0100 Subject: [PATCH] Days (Monday thru Friday) are now shown on the canteen screen --- .../23.json | 393 ++++++++++++++++++ .../denizk0461/weserplaner/data/StwParser.kt | 8 +- .../denizk0461/weserplaner/db/AppDatabase.kt | 11 +- .../weserplaner/fragment/CanteenFragment.kt | 6 +- .../denizk0461/weserplaner/model/OfferDate.kt | 2 + .../weserplaner/values/SettingsPreferences.kt | 5 + 6 files changed, 419 insertions(+), 6 deletions(-) create mode 100644 app/schemas/com.denizk0461.weserplaner.db.AppDatabase/23.json diff --git a/app/schemas/com.denizk0461.weserplaner.db.AppDatabase/23.json b/app/schemas/com.denizk0461.weserplaner.db.AppDatabase/23.json new file mode 100644 index 0000000..7340f09 --- /dev/null +++ b/app/schemas/com.denizk0461.weserplaner.db.AppDatabase/23.json @@ -0,0 +1,393 @@ +{ + "formatVersion": 1, + "database": { + "version": 23, + "identityHash": "8e471de9f5f6c5f301676106d46ed5a1", + "entities": [ + { + "tableName": "timetables", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "studip_events", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`eventId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timetableId` INTEGER NOT NULL, `title` TEXT NOT NULL, `lecturer` TEXT NOT NULL, `room` TEXT NOT NULL, `day` INTEGER NOT NULL, `timeslotStart` TEXT NOT NULL, `timeslotEnd` TEXT NOT NULL, `timeslotId` INTEGER NOT NULL, `colour` INTEGER NOT NULL, FOREIGN KEY(`timetableId`) REFERENCES `timetables`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "eventId", + "columnName": "eventId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timetableId", + "columnName": "timetableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lecturer", + "columnName": "lecturer", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "day", + "columnName": "day", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeslotStart", + "columnName": "timeslotStart", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timeslotEnd", + "columnName": "timeslotEnd", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timeslotId", + "columnName": "timeslotId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colour", + "columnName": "colour", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "eventId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "timetables", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "timetableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "offer_date", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `day` TEXT NOT NULL, `date` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "day", + "columnName": "day", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offer_canteen", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `canteen` TEXT NOT NULL, `openingHours` TEXT NOT NULL, `news` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canteen", + "columnName": "canteen", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "openingHours", + "columnName": "openingHours", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "news", + "columnName": "news", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "offer_category", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `dateId` INTEGER NOT NULL, `canteenId` INTEGER NOT NULL, `category` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`dateId`) REFERENCES `offer_date`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`canteenId`) REFERENCES `offer_canteen`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateId", + "columnName": "dateId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canteenId", + "columnName": "canteenId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "offer_date", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dateId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "offer_canteen", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "canteenId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "offer_item", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER NOT NULL, `categoryId` INTEGER NOT NULL, `title` TEXT NOT NULL, `price` TEXT NOT NULL, `dietary_preferences` TEXT NOT NULL, `allergens` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`categoryId`) REFERENCES `offer_category`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dietaryPreferences", + "columnName": "dietary_preferences", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "allergens", + "columnName": "allergens", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "offer_category", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "categoryId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "event_tasks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`taskId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `eventId` INTEGER NOT NULL, `dueDate` INTEGER NOT NULL, `notifyDate` INTEGER NOT NULL, `title` TEXT NOT NULL, `notes` TEXT NOT NULL, `room` TEXT NOT NULL, `isFinished` INTEGER NOT NULL, FOREIGN KEY(`eventId`) REFERENCES `studip_events`(`eventId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "taskId", + "columnName": "taskId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventId", + "columnName": "eventId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dueDate", + "columnName": "dueDate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notifyDate", + "columnName": "notifyDate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notes", + "columnName": "notes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isFinished", + "columnName": "isFinished", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "taskId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "studip_events", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "eventId" + ], + "referencedColumns": [ + "eventId" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8e471de9f5f6c5f301676106d46ed5a1')" + ] + } +} \ No newline at end of file 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 b7a3147..975db12 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/data/StwParser.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/data/StwParser.kt @@ -254,6 +254,9 @@ class StwParser(application: Application) { * @return date object */ private fun Document.getDateByIndex(index: Int): OfferDate { + val day = getElementsByClass("tabs")[0] + .getElementsByClass("tab-title")[index].text() + /* * Fetch the date for a given day. It will be in the following format: * 24. Apr @@ -272,7 +275,7 @@ class StwParser(application: Application) { val date = "${rawDate[0]}${rawDate[1].monthToNumber()}." // Save the date to its list - return OfferDate(dateId, date) + return OfferDate(dateId, day, date) } /** @@ -438,7 +441,8 @@ class StwParser(application: Application) { * Cleans up a HTML source by doing the following: * - replace non-breaking spaces ' ' with a regular space character ' ', * - replace closing paragraph tags '

' with a line break '\n', and - * - remove any other tag. + * - remove any other tag that does not start with 'a' or 'b' – this is a hacky way to preserve + * and
tags. * This is used to format the opening hours of the canteens. * * @receiver Element to parse the text of diff --git a/app/src/main/java/com/denizk0461/weserplaner/db/AppDatabase.kt b/app/src/main/java/com/denizk0461/weserplaner/db/AppDatabase.kt index d445aa9..614d70b 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/db/AppDatabase.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/db/AppDatabase.kt @@ -22,7 +22,7 @@ import com.denizk0461.weserplaner.model.* OfferItem::class, EventTask::class, ], - version = 22, + version = 23, exportSchema = true, autoMigrations = [ AutoMigration(from = 20, to = 21), @@ -61,6 +61,7 @@ abstract class AppDatabase : RoomDatabase() { ).addMigrations( migrationAddNewsToOfferCanteen_19_20, migration_21_22, + migration_22_23, // migrationAddTimetableId_20_21, // migrationAddEventTask_20_21, ) @@ -125,5 +126,13 @@ abstract class AppDatabase : RoomDatabase() { database.execSQL("ALTER TABLE studip_events_temp RENAME TO studip_events") } } + + private val migration_22_23 = object : Migration(22, 23) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + ALTER TABLE offer_date ADD COLUMN day TEXT NOT NULL DEFAULT '?' + """) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/weserplaner/fragment/CanteenFragment.kt b/app/src/main/java/com/denizk0461/weserplaner/fragment/CanteenFragment.kt index 0588ea9..faafcc8 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/fragment/CanteenFragment.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/fragment/CanteenFragment.kt @@ -40,7 +40,7 @@ class CanteenFragment : AppFragment() { /** * Locally stored dates to populate the TabLayout with. */ - private var dates: List = listOf() + private var dates: List = listOf() // Instantiate the view binding override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -219,7 +219,7 @@ class CanteenFragment : AppFragment() { viewModel.getDates().observe(viewLifecycleOwner) { newDates -> // Retrieve the dates individually and store them for the tab mediator to use - dates = newDates.map { it.date } + dates = newDates // Let the adapter know of the new amount of dates (pages) to display viewPagerAdapter.itemCount = dates.size @@ -230,7 +230,7 @@ class CanteenFragment : AppFragment() { // Set up TabLayoutMediator to populate tabs TabLayoutMediator(binding.dayTabLayout, binding.viewPager) { tab, position -> - tab.text = dates[position] + tab.text = dates[position].run { "$day, $date" } }.attach() // Set extended FAB to extend when the page is changed diff --git a/app/src/main/java/com/denizk0461/weserplaner/model/OfferDate.kt b/app/src/main/java/com/denizk0461/weserplaner/model/OfferDate.kt index 0c54d1b..baabc00 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/model/OfferDate.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/model/OfferDate.kt @@ -7,6 +7,7 @@ import androidx.room.PrimaryKey * Entity for binding several canteen items to a specific date. Registered in the app database. * * @param id primary key that uniquely identifies the item + * @param day the day. that's it. pretty self-explanatory, eh? * @param date date formatted as dd.MM. */ @Entity( @@ -14,5 +15,6 @@ import androidx.room.PrimaryKey ) data class OfferDate( @PrimaryKey val id: Int, + val day: String, val date: String, ) diff --git a/app/src/main/java/com/denizk0461/weserplaner/values/SettingsPreferences.kt b/app/src/main/java/com/denizk0461/weserplaner/values/SettingsPreferences.kt index ce46777..de961f6 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/values/SettingsPreferences.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/values/SettingsPreferences.kt @@ -88,5 +88,10 @@ enum class SettingsPreferences(val key: String) { * Which timetable the user has selected. */ SELECTED_TIMETABLE("selected_timetable"), + + /** + * How the dates should be shown on the canteen screen. + */ + CANTEEN_DATE_PREFERENCE("date_canteen_preference") ; } \ No newline at end of file