From 46987c6fa542adaa621d956864cb97a7214957c3 Mon Sep 17 00:00:00 2001 From: sweetbread Date: Sun, 26 May 2024 15:58:07 +0300 Subject: [PATCH] feat(cache): Add conflict strategy --- app/src/main/java/ru/sweetbread/unn/API.kt | 6 +- .../java/ru/sweetbread/unn/db/Schedule.kt | 298 +++++++++++------- 2 files changed, 188 insertions(+), 116 deletions(-) diff --git a/app/src/main/java/ru/sweetbread/unn/API.kt b/app/src/main/java/ru/sweetbread/unn/API.kt index 02864e0..205de5b 100644 --- a/app/src/main/java/ru/sweetbread/unn/API.kt +++ b/app/src/main/java/ru/sweetbread/unn/API.kt @@ -10,9 +10,10 @@ import io.ktor.http.parameters import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.json.JSONArray import org.json.JSONObject -import ru.sweetbread.unn.db.cacheScheduleItems +import ru.sweetbread.unn.db.cacheSchedule import ru.sweetbread.unn.db.cacheUser import ru.sweetbread.unn.db.loadSchedule import ru.sweetbread.unn.db.loadUserByBitrixId @@ -207,6 +208,7 @@ suspend fun getScheduleDay( if ((type == ME.type) and (id == ME.unnId!!)) { val schedule = withContext(Dispatchers.IO) { loadSchedule(date) } + Log.d("Schedule", schedule.joinToString()) if (schedule.size != 0) { return schedule } @@ -294,7 +296,7 @@ suspend fun getSchedule( } if ((type == ME.type) and (id == ME.unnId!!)) { - cacheScheduleItems(out) + cacheSchedule(out) } return out } diff --git a/app/src/main/java/ru/sweetbread/unn/db/Schedule.kt b/app/src/main/java/ru/sweetbread/unn/db/Schedule.kt index 9da3a94..7f13148 100644 --- a/app/src/main/java/ru/sweetbread/unn/db/Schedule.kt +++ b/app/src/main/java/ru/sweetbread/unn/db/Schedule.kt @@ -1,10 +1,12 @@ package ru.sweetbread.unn.db +import android.util.Log import androidx.room.ColumnInfo import androidx.room.Dao import androidx.room.Delete import androidx.room.Entity import androidx.room.Insert +import androidx.room.OnConflictStrategy import androidx.room.PrimaryKey import androidx.room.Query import ru.sweetbread.unn.Auditorium @@ -16,6 +18,7 @@ import ru.sweetbread.unn.LecturerRank import ru.sweetbread.unn.ScheduleUnit import ru.sweetbread.unn.ui.layout.db import java.time.LocalDate +import java.time.LocalDateTime import java.time.LocalTime import java.time.format.DateTimeFormatter @@ -23,15 +26,16 @@ import java.time.format.DateTimeFormatter data class BuildingDB( @PrimaryKey val oid: Int, @ColumnInfo val name: String, - @ColumnInfo val gid: Int + @ColumnInfo val gid: Int, + @ColumnInfo val expiredAt: String ) @Dao interface BuildingDao { @Query("SELECT * FROM buildingdb WHERE oid = :oid LIMIT 1") - fun get(oid: Int): BuildingDB + fun get(oid: Int): BuildingDB? - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(building: BuildingDB) @Delete @@ -39,20 +43,26 @@ interface BuildingDao { } fun cacheBuilding(building: Building) { - try { - db.buildingDao().insert( - BuildingDB( - building.oid, - building.name, - building.gid - ) + db.buildingDao().insert( + BuildingDB( + building.oid, + building.name, + building.gid, + LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME) ) - } catch (_: android.database.sqlite.SQLiteConstraintException) { - } + ) } -fun loadBuilding(oid: Int): Building { - return db.buildingDao().get(oid).let { +fun loadBuilding(oid: Int): Building? { + return db.buildingDao().get(oid)?.let { + if (LocalDateTime.parse( + it.expiredAt, + DateTimeFormatter.ISO_DATE_TIME + ) > LocalDateTime.now() + ) { + db.buildingDao().delete(it) + return null + } Building( it.name, it.gid, @@ -66,15 +76,16 @@ data class AuditoriumDB( @PrimaryKey val oid: Int, @ColumnInfo val name: String, @ColumnInfo val floor: Int?, - @ColumnInfo val buildingOid: Int + @ColumnInfo val buildingOid: Int, + @ColumnInfo val expiredAt: String ) @Dao interface AuditoriumDao { @Query("SELECT * FROM auditoriumdb WHERE oid = :oid LIMIT 1") - fun get(oid: Int): AuditoriumDB + fun get(oid: Int): AuditoriumDB? - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(auditorium: AuditoriumDB) @Delete @@ -82,27 +93,34 @@ interface AuditoriumDao { } fun cacheAuditorium(auditorium: Auditorium) { - try { - cacheBuilding(auditorium.building) - db.auditoriumDao().insert( - AuditoriumDB( - auditorium.oid, - auditorium.name, - auditorium.floor, - auditorium.building.oid - ) + cacheBuilding(auditorium.building) + db.auditoriumDao().insert( + AuditoriumDB( + auditorium.oid, + auditorium.name, + auditorium.floor, + auditorium.building.oid, + LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME) ) - } catch (_: android.database.sqlite.SQLiteConstraintException) { - } + ) } -fun loadAuditorium(oid: Int): Auditorium { - return db.auditoriumDao().get(oid).let { - Auditorium( +fun loadAuditorium(oid: Int): Auditorium? { + return db.auditoriumDao().get(oid)?.let { + if (LocalDateTime.parse( + it.expiredAt, + DateTimeFormatter.ISO_DATE_TIME + ) > LocalDateTime.now() + ) { + db.auditoriumDao().delete(it) + return null + } + val building = loadBuilding(it.buildingOid) ?: return null + return Auditorium( it.name, it.oid, it.floor ?: 0, - loadBuilding(it.buildingOid) + building ) } } @@ -111,15 +129,16 @@ fun loadAuditorium(oid: Int): Auditorium { data class DisciplineDB( @PrimaryKey val oid: Int, @ColumnInfo val name: String, - @ColumnInfo val type: Int + @ColumnInfo val type: Int, + @ColumnInfo val expiredAt: String ) @Dao interface DisciplineDao { @Query("SELECT * FROM disciplinedb WHERE oid = :oid LIMIT 1") - fun get(oid: Int): DisciplineDB + fun get(oid: Int): DisciplineDB? - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(discipline: DisciplineDB) @Delete @@ -127,21 +146,28 @@ interface DisciplineDao { } fun cacheDiscipline(discipline: Discipline) { - try { - db.disciplineDao().insert( - DisciplineDB( - discipline.oid, - discipline.name, - discipline.type - ) + db.disciplineDao().insert( + DisciplineDB( + discipline.oid, + discipline.name, + discipline.type, + LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME) ) - } catch (_: android.database.sqlite.SQLiteConstraintException) { - } + ) } -fun loadDiscipline(oid: Int): Discipline { - return db.disciplineDao().get(oid).let { - Discipline( +fun loadDiscipline(oid: Int): Discipline? { + return db.disciplineDao().get(oid)?.let { + if (LocalDateTime.parse( + it.expiredAt, + DateTimeFormatter.ISO_DATE_TIME + ) > LocalDateTime.now() + ) { + db.disciplineDao().delete(it) + return null + } + + return Discipline( it.name, it.oid, it.type @@ -154,15 +180,16 @@ data class KindOfWorkDB( @PrimaryKey val oid: Int, @ColumnInfo val name: String, @ColumnInfo val uid: String, - @ColumnInfo val complexity: Int + @ColumnInfo val complexity: Int, + @ColumnInfo val expiredAt: String ) @Dao interface KindOfWorkDao { @Query("SELECT * FROM kindofworkdb WHERE oid = :oid LIMIT 1") - fun get(oid: Int): KindOfWorkDB + fun get(oid: Int): KindOfWorkDB? - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(kindOfWork: KindOfWorkDB) @Delete @@ -170,22 +197,29 @@ interface KindOfWorkDao { } fun cacheKindOfWork(kindOfWork: KindOfWork) { - try { - db.kindOfWorkDao().insert( - KindOfWorkDB( - kindOfWork.oid, - kindOfWork.name, - kindOfWork.uid, - kindOfWork.complexity - ) + db.kindOfWorkDao().insert( + KindOfWorkDB( + kindOfWork.oid, + kindOfWork.name, + kindOfWork.uid, + kindOfWork.complexity, + LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME) ) - } catch (_: android.database.sqlite.SQLiteConstraintException) { - } + ) } -fun loadKindOfWork(oid: Int): KindOfWork { - return db.kindOfWorkDao().get(oid).let { - KindOfWork( +fun loadKindOfWork(oid: Int): KindOfWork? { + return db.kindOfWorkDao().get(oid)?.let { + if (LocalDateTime.parse( + it.expiredAt, + DateTimeFormatter.ISO_DATE_TIME + ) > LocalDateTime.now() + ) { + db.kindOfWorkDao().delete(it) + return null + } + + return KindOfWork( it.name, it.oid, it.uid, @@ -200,15 +234,16 @@ data class LecturerDB( @ColumnInfo val name: String, @ColumnInfo val rank: LecturerRank, @ColumnInfo val email: String, - @ColumnInfo val uid: String + @ColumnInfo val uid: String, + @ColumnInfo val expiredAt: String ) @Dao interface LecturerDao { @Query("SELECT * FROM lecturerdb WHERE unnId = :unnId LIMIT 1") - fun get(unnId: Int): LecturerDB + fun get(unnId: Int): LecturerDB? - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(lecturer: LecturerDB) @Delete @@ -216,23 +251,30 @@ interface LecturerDao { } fun cacheLecturer(lecturer: Lecturer) { - try { - db.lecturerDao().insert( - LecturerDB( - lecturer.unnId, - lecturer.name, - lecturer.rank, - lecturer.email, - lecturer.uid - ) + db.lecturerDao().insert( + LecturerDB( + lecturer.unnId, + lecturer.name, + lecturer.rank, + lecturer.email, + lecturer.uid, + LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME) ) - } catch (_: android.database.sqlite.SQLiteConstraintException) { - } + ) } -fun loadLecturer(unnId: Int): Lecturer { - return db.lecturerDao().get(unnId).let { - Lecturer( +fun loadLecturer(unnId: Int): Lecturer? { + return db.lecturerDao().get(unnId)?.let { + if (LocalDateTime.parse( + it.expiredAt, + DateTimeFormatter.ISO_DATE_TIME + ) > LocalDateTime.now() + ) { + db.lecturerDao().delete(it) + return null + } + + return Lecturer( it.name, it.rank, it.email, @@ -253,62 +295,90 @@ data class ScheduleUnitDB( @ColumnInfo val auditoriumOid: Int, @ColumnInfo val disciplineOid: Int, @ColumnInfo val kindOfWorkOid: Int, - @ColumnInfo val lecturerId: Int // TODO: many-to-many + @ColumnInfo val lecturerId: Int, // TODO: many-to-many + + @ColumnInfo val expiredAt: String ) @Dao interface ScheduleItemDao { @Query("SELECT * FROM scheduleUnitDB WHERE oid = :oid LIMIT 1") - fun getScheduleItem(oid: Int): ScheduleUnitDB? + fun getSchedule(oid: Int): ScheduleUnitDB? @Query("SELECT * FROM scheduleUnitDB WHERE date = :date ORDER BY `begin`") fun getSchedule(date: String): List - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(user: ScheduleUnitDB) @Delete fun delete(user: ScheduleUnitDB) } -fun cacheScheduleItems(items: ArrayList) { - for (item in items) { - try { - cacheAuditorium(item.auditorium) - cacheDiscipline(item.discipline) - cacheKindOfWork(item.kindOfWork) - cacheLecturer(item.lecturers[0]) +fun cacheSchedule(item: ScheduleUnit) { + cacheAuditorium(item.auditorium) + cacheDiscipline(item.discipline) + cacheKindOfWork(item.kindOfWork) + cacheLecturer(item.lecturers[0]) - db.scheduleDao().insert( - ScheduleUnitDB( - item.oid, - item.date.format(DateTimeFormatter.ISO_DATE), - item.stream, - item.begin.format(DateTimeFormatter.ISO_TIME), - item.end.format(DateTimeFormatter.ISO_TIME), - item.auditorium.oid, - item.discipline.oid, - item.kindOfWork.oid, - item.lecturers[0].unnId - ) - ) - } catch (_: android.database.sqlite.SQLiteConstraintException) { - } - } + db.scheduleDao().insert( + ScheduleUnitDB( + item.oid, + item.date.format(DateTimeFormatter.ISO_DATE), + item.stream, + item.begin.format(DateTimeFormatter.ISO_TIME), + item.end.format(DateTimeFormatter.ISO_TIME), + item.auditorium.oid, + item.discipline.oid, + item.kindOfWork.oid, + item.lecturers[0].unnId, + when { + (LocalDate.now() > item.date) -> LocalDateTime.now().plusWeeks(2) + (LocalDate.now().plusWeeks(1) > item.date) -> LocalDateTime.now().plusDays(1) + (LocalDate.now().plusWeeks(2) > item.date) -> LocalDateTime.now().plusWeeks(1) + else -> LocalDateTime.now().plusWeeks(2) + }.format(DateTimeFormatter.ISO_DATE_TIME) + ) + ) } -fun loadSchedule(date: LocalDate): ArrayList { - return ArrayList(db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE)).map { - ScheduleUnit( +fun cacheSchedule(items: ArrayList) { + for (item in items) + cacheSchedule(item) +} + +fun loadSchedule(oid: Int): ScheduleUnit? { + db.scheduleDao().getSchedule(oid)?.let { + Log.d("load", it.oid.toString()) + if (LocalDateTime.parse( + it.expiredAt, + DateTimeFormatter.ISO_DATE_TIME + ) < LocalDateTime.now() + ) { + Log.d("delete", it.oid.toString()) + db.scheduleDao().delete(it) + return null + } + + return ScheduleUnit( it.oid, - loadAuditorium(it.auditoriumOid), + loadAuditorium(it.auditoriumOid) ?: return null, LocalDate.parse(it.date, DateTimeFormatter.ISO_DATE), - loadDiscipline(it.disciplineOid), - loadKindOfWork(it.kindOfWorkOid), - arrayListOf(loadLecturer(it.lecturerId)), + loadDiscipline(it.disciplineOid) ?: return null, + loadKindOfWork(it.kindOfWorkOid) ?: return null, + arrayListOf(loadLecturer(it.lecturerId) ?: return null), it.stream, LocalTime.parse(it.begin, DateTimeFormatter.ISO_TIME), LocalTime.parse(it.end, DateTimeFormatter.ISO_TIME) ) - }) + } + return null +} +fun loadSchedule(date: LocalDate): ArrayList { + db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE)) + .mapNotNull { Log.d("meow", "${it.oid}: ${loadSchedule(it.oid)}") } + return ArrayList( + db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE)) + .mapNotNull { loadSchedule(it.oid) } + ) } \ No newline at end of file