Add clearing all tables dependent on classId

This commit is contained in:
Faierbel 2024-04-21 04:01:21 +02:00
parent 2a0ac7f91e
commit 157e04b239
23 changed files with 207 additions and 84 deletions

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.School import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@ -11,5 +12,16 @@ import javax.inject.Singleton
interface SchoolDao : BaseDao<School> { interface SchoolDao : BaseDao<School> {
@Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId") @Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId")
fun load(studentId: Int, classId: Int): Flow<School?> fun loadWithClassId(studentId: Int, classId: Int): Flow<School?>
@Query("SELECT * FROM School WHERE student_id = :studentId")
fun loadNoClassId(studentId: Int): Flow<School?>
fun load(student: Student): Flow<School?> {
return if (student.isEduOne == true) {
loadNoClassId(student.studentId)
} else {
loadWithClassId(student.studentId, student.classId)
}
}
} }

View File

@ -16,10 +16,16 @@ interface SemesterDao : BaseDao<Semester> {
suspend fun insertSemesters(items: List<Semester>): List<Long> suspend fun insertSemesters(items: List<Semester>): List<Long>
@Query("SELECT * FROM Semesters WHERE (student_id = :studentId AND class_id = :classId)") @Query("SELECT * FROM Semesters WHERE (student_id = :studentId AND class_id = :classId)")
suspend fun loadAll(studentId: Int, classId: Int): List<Semester> suspend fun loadAllWithClassId(studentId: Int, classId: Int): List<Semester>
@Query("SELECT * FROM Semesters WHERE student_id = :studentId")
suspend fun loadAllNoClassId(studentId: Int): List<Semester>
suspend fun loadAll(student: Student): List<Semester> { suspend fun loadAll(student: Student): List<Semester> {
val classId = if (student.isEduOne == true) 0 else student.classId return if (student.isEduOne == true) {
return loadAll(student.studentId, classId) loadAllNoClassId(student.studentId)
} else {
loadAllWithClassId(student.studentId, student.classId)
}
} }
} }

View File

@ -47,13 +47,9 @@ abstract class StudentDao {
abstract suspend fun loadAll(): List<Student> abstract suspend fun loadAll(): List<Student>
@Transaction @Transaction
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0 AND Students.is_edu_one = 1)") @Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id)")
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>> abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
@Transaction
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0 AND Students.is_edu_one = 1) WHERE Students.id = :id")
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
@Query("UPDATE Students SET is_current = 1 WHERE id = :id") @Query("UPDATE Students SET is_current = 1 WHERE id = :id")
abstract suspend fun updateCurrent(id: Long) abstract suspend fun updateCurrent(id: Long)

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Teacher import io.github.wulkanowy.data.db.entities.Teacher
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@ -11,5 +12,16 @@ import javax.inject.Singleton
interface TeacherDao : BaseDao<Teacher> { interface TeacherDao : BaseDao<Teacher> {
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId") @Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId")
fun loadAll(studentId: Int, classId: Int): Flow<List<Teacher>> fun loadAllWithClassId(studentId: Int, classId: Int): Flow<List<Teacher>>
@Query("SELECT * FROM Teachers WHERE student_id = :studentId")
fun loadAllNoClassId(studentId: Int): Flow<List<Teacher>>
fun loadAll(student: Student): Flow<List<Teacher>> {
return if (student.isEduOne == true) {
loadAllNoClassId(student.studentId)
} else {
loadAllWithClassId(student.studentId, student.classId)
}
}
} }

View File

@ -6,6 +6,15 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration63 : AutoMigrationSpec { class Migration63 : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) { override fun onPostMigrate(db: SupportSQLiteDatabase) {
db.execSQL("UPDATE Students SET is_edu_one = NULL WHERE is_edu_one = 0") db.execSQL("DROP TABLE IF EXISTS `Semesters`")
db.execSQL("DROP TABLE IF EXISTS `School`")
db.execSQL("DROP TABLE IF EXISTS `Teachers`")
db.execSQL("CREATE TABLE IF NOT EXISTS `Semesters` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)")
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `Semesters` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)")
db.execSQL("CREATE TABLE IF NOT EXISTS `School` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)")
db.execSQL("CREATE TABLE IF NOT EXISTS `Teachers` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)")
db.execSQL("UPDATE Students SET is_edu_one = NULL")
} }
} }

View File

@ -0,0 +1,4 @@
package io.github.wulkanowy.data.exceptions
class NoSuchStudentException(id: Long) :
Exception("There is no student with id $id in database")

View File

@ -36,7 +36,7 @@ class SchoolRepository @Inject constructor(
) )
it == null || forceRefresh || isExpired it == null || forceRefresh || isExpired
}, },
query = { schoolDb.load(semester.studentId, semester.classId) }, query = { schoolDb.load(student) },
fetch = { fetch = {
wulkanowySdkFactory.create(student, semester) wulkanowySdkFactory.create(student, semester)
.getSchool() .getSchool()

View File

@ -12,6 +12,7 @@ import io.github.wulkanowy.data.db.entities.StudentName
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.data.exceptions.NoSuchStudentException
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.mappers.mapToPojo import io.github.wulkanowy.data.mappers.mapToPojo
import io.github.wulkanowy.data.pojos.RegisterUser import io.github.wulkanowy.data.pojos.RegisterUser
@ -65,7 +66,8 @@ class StudentRepository @Inject constructor(
.mapToPojo(password) .mapToPojo(password)
.also { it.logErrors() } .also { it.logErrors() }
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> { @Deprecated("Semesters are not synced within this method and students with empty semesters are not returned")
suspend fun getSavedStudentsWithSemesters(decryptPass: Boolean = true): List<StudentWithSemesters> {
return studentDb.loadStudentsWithSemesters().map { (student, semesters) -> return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
StudentWithSemesters( StudentWithSemesters(
student = student.apply { student = student.apply {
@ -80,22 +82,25 @@ class StudentRepository @Inject constructor(
} }
} }
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true): StudentWithSemesters? = suspend fun getSavedStudents(decryptPass: Boolean = true): List<Student> {
studentDb.loadStudentWithSemestersById(id).let { res -> val students = studentDb.loadAll()
StudentWithSemesters( if (!decryptPass) return students
student = res.keys.firstOrNull() ?: return null,
semesters = res.values.first(), return students.map { student ->
) if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
}.apply { return@map student
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { }
student.password = withContext(dispatchers.io) {
student.apply {
password = withContext(dispatchers.io) {
scrambler.decrypt(student.password) scrambler.decrypt(student.password)
} }
} }
} }
}
suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student { suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student {
val student = studentDb.loadById(id) ?: throw NoCurrentStudentException() val student = studentDb.loadById(id) ?: throw NoSuchStudentException(id)
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) { student.password = withContext(dispatchers.io) {
@ -181,8 +186,8 @@ class StudentRepository @Inject constructor(
} }
} }
suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) { suspend fun switchStudent(student: Student) {
studentDb.switchCurrent(studentWithSemesters.student.id) studentDb.switchCurrent(student.id)
} }
suspend fun logoutStudent(student: Student) = studentDb.delete(student) suspend fun logoutStudent(student: Student) = studentDb.delete(student)
@ -190,8 +195,8 @@ class StudentRepository @Inject constructor(
suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) = suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) =
studentDb.update(studentNickAndAvatar) studentDb.update(studentNickAndAvatar)
suspend fun isOneUniqueStudent() = getSavedStudents(false) suspend fun isOneUniqueStudent() = studentDb.loadAll()
.distinctBy { it.student.studentName }.size == 1 .distinctBy { it.studentName }.size == 1
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) = suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
wulkanowySdkFactory.create(student, semester) wulkanowySdkFactory.create(student, semester)
@ -199,7 +204,7 @@ class StudentRepository @Inject constructor(
suspend fun refreshStudentAfterAuthorize(student: Student, semester: Semester) { suspend fun refreshStudentAfterAuthorize(student: Student, semester: Semester) {
val wulkanowySdk = wulkanowySdkFactory.create(student, semester) val wulkanowySdk = wulkanowySdkFactory.create(student, semester)
val newCurrentApiStudent = runCatching { wulkanowySdk.getCurrentStudent() } val newCurrentApiStudent = runCatching { wulkanowySdk.getCurrentStudent() }
.onFailure { Timber.e(it, "Can't find student with id ${student.studentId}") } .onFailure { Timber.e(it, "Can't find student with id ${student.studentId}") }
.getOrNull() ?: return .getOrNull() ?: return

View File

@ -35,7 +35,7 @@ class TeacherRepository @Inject constructor(
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
it.isEmpty() || forceRefresh || isExpired it.isEmpty() || forceRefresh || isExpired
}, },
query = { teacherDb.loadAll(semester.studentId, semester.classId) }, query = { teacherDb.loadAll(student) },
fetch = { fetch = {
wulkanowySdkFactory.create(student, semester) wulkanowySdkFactory.create(student, semester)
.getTeachers() .getTeachers()

View File

@ -33,7 +33,7 @@ class AccountPresenter @Inject constructor(
} }
private fun loadData() { private fun loadData() {
resourceFlow { studentRepository.getSavedStudents(false) } resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }
.logResourceStatus("load account data") .logResourceStatus("load account data")
.onResourceSuccess { view?.updateData(createAccountItems(it)) } .onResourceSuccess { view?.updateData(createAccountItems(it)) }
.onResourceError(errorHandler::dispatch) .onResourceError(errorHandler::dispatch)

View File

@ -1,9 +1,15 @@
package io.github.wulkanowy.ui.modules.account.accountdetails package io.github.wulkanowy.ui.modules.account.accountdetails
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceLoading
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
@ -14,6 +20,7 @@ import javax.inject.Inject
class AccountDetailsPresenter @Inject constructor( class AccountDetailsPresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val semeRepository: SemesterRepository,
private val syncManager: SyncManager private val syncManager: SyncManager
) : BasePresenter<AccountDetailsView>(errorHandler, studentRepository) { ) : BasePresenter<AccountDetailsView>(errorHandler, studentRepository) {
@ -46,7 +53,12 @@ class AccountDetailsPresenter @Inject constructor(
} }
private fun loadData() { private fun loadData() {
resourceFlow { studentRepository.getSavedStudentById(studentId ?: -1) } resourceFlow {
val student = studentRepository.getStudentById(studentId ?: -1)
val semesters = semeRepository.getSemesters(student)
StudentWithSemesters(student, semesters)
}
.logResourceStatus("loading account details view") .logResourceStatus("loading account details view")
.onResourceLoading { .onResourceLoading {
view?.run { view?.run {
@ -85,7 +97,7 @@ class AccountDetailsPresenter @Inject constructor(
Timber.i("Select student ${studentWithSemesters!!.student.id}") Timber.i("Select student ${studentWithSemesters!!.student.id}")
resourceFlow { studentRepository.switchStudent(studentWithSemesters!!) } resourceFlow { studentRepository.switchStudent(studentWithSemesters!!.student) }
.logResourceStatus("change student") .logResourceStatus("change student")
.onResourceSuccess { view?.recreateMainView() } .onResourceSuccess { view?.recreateMainView() }
.onResourceNotLoading { view?.popViewToMain() } .onResourceNotLoading { view?.popViewToMain() }
@ -122,10 +134,12 @@ class AccountDetailsPresenter @Inject constructor(
syncManager.stopSyncWorker() syncManager.stopSyncWorker()
openClearLoginView() openClearLoginView()
} }
studentWithSemesters?.student?.isCurrent == true -> { studentWithSemesters?.student?.isCurrent == true -> {
Timber.i("Logout result: Logout student and switch to another") Timber.i("Logout result: Logout student and switch to another")
recreateMainView() recreateMainView()
} }
else -> { else -> {
Timber.i("Logout result: Logout student") Timber.i("Logout result: Logout student")
recreateMainView() recreateMainView()

View File

@ -1,8 +1,12 @@
package io.github.wulkanowy.ui.modules.account.accountquick package io.github.wulkanowy.ui.modules.account.accountquick
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.account.AccountItem import io.github.wulkanowy.ui.modules.account.AccountItem
@ -40,7 +44,7 @@ class AccountQuickPresenter @Inject constructor(
return return
} }
resourceFlow { studentRepository.switchStudent(studentWithSemesters) } resourceFlow { studentRepository.switchStudent(studentWithSemesters.student) }
.logResourceStatus("change student") .logResourceStatus("change student")
.onResourceSuccess { view?.recreateMainView() } .onResourceSuccess { view?.recreateMainView() }
.onResourceNotLoading { view?.popView() } .onResourceNotLoading { view?.popView() }

View File

@ -69,7 +69,7 @@ class LoginStudentSelectPresenter @Inject constructor(
private fun loadData() { private fun loadData() {
resetSelectedState() resetSelectedState()
resourceFlow { studentRepository.getSavedStudents(false) }.onEach { resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }.onEach {
students = it.dataOrNull.orEmpty() students = it.dataOrNull.orEmpty()
when (it) { when (it) {
is Resource.Loading -> Timber.d("Login student select students load started") is Resource.Loading -> Timber.d("Login student select students load started")

View File

@ -35,7 +35,7 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
} }
private fun loadData() { private fun loadData() {
resourceFlow { studentRepository.getSavedStudents(false) }.onEach { resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }.onEach {
when (it) { when (it) {
is Resource.Loading -> Timber.d("Lucky number widget configure students data load") is Resource.Loading -> Timber.d("Lucky number widget configure students data load")
is Resource.Success -> { is Resource.Success -> {

View File

@ -132,7 +132,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking { private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking {
try { try {
val students = studentRepository.getSavedStudents() val students = studentRepository.getSavedStudents()
val student = students.singleOrNull { it.student.id == studentId }?.student val student = students.singleOrNull { it.id == studentId }
val currentStudent = when { val currentStudent = when {
student != null -> student student != null -> student
studentId != 0L && studentRepository.isCurrentStudentSet() -> { studentId != 0L && studentRepository.isCurrentStudentSet() -> {

View File

@ -85,7 +85,7 @@ class MainPresenter @Inject constructor(
return return
} }
resourceFlow { studentRepository.getSavedStudents(false) } resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }
.logResourceStatus("load student avatar") .logResourceStatus("load student avatar")
.onResourceSuccess { .onResourceSuccess {
studentsWitSemesters = it studentsWitSemesters = it

View File

@ -42,22 +42,24 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
} }
private fun loadData() { private fun loadData() {
resourceFlow { studentRepository.getSavedStudents(false) }.onEach { resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }
when (it) { .onEach {
is Resource.Loading -> Timber.d("Timetable widget configure students data load") when (it) {
is Resource.Success -> { is Resource.Loading -> Timber.d("Timetable widget configure students data load")
val selectedStudentId = appWidgetId?.let { id -> is Resource.Success -> {
sharedPref.getLong(getStudentWidgetKey(id), 0) val selectedStudentId = appWidgetId?.let { id ->
} ?: -1 sharedPref.getLong(getStudentWidgetKey(id), 0)
when { } ?: -1
it.data.isEmpty() -> view?.openLoginView() when {
it.data.size == 1 && !isFromProvider -> onItemSelect(it.data.single().student) it.data.isEmpty() -> view?.openLoginView()
else -> view?.updateData(it.data, selectedStudentId) it.data.size == 1 && !isFromProvider -> onItemSelect(it.data.single().student)
else -> view?.updateData(it.data, selectedStudentId)
}
} }
is Resource.Error -> errorHandler.dispatch(it.error)
} }
is Resource.Error -> errorHandler.dispatch(it.error) }.launch()
}
}.launch()
} }
private fun registerStudent(student: Student?) { private fun registerStudent(student: Student?) {

View File

@ -95,7 +95,7 @@ class TimetableWidgetFactory(
private suspend fun getStudent(studentId: Long): Student? { private suspend fun getStudent(studentId: Long): Student? {
val students = studentRepository.getSavedStudents() val students = studentRepository.getSavedStudents()
return students.singleOrNull { it.student.id == studentId }?.student return students.singleOrNull { it.id == studentId }
} }
private suspend fun getLessons( private suspend fun getLessons(

View File

@ -2,7 +2,11 @@ package io.github.wulkanowy.ui.modules.timetablewidget
import android.app.PendingIntent import android.app.PendingIntent
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetManager.* import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -22,7 +26,14 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.widgets.TimetableWidgetService import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.* import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.capitalise
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -244,7 +255,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try { private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try {
val students = studentRepository.getSavedStudents(false) val students = studentRepository.getSavedStudents(false)
val student = students.singleOrNull { it.student.id == studentId }?.student val student = students.singleOrNull { it.id == studentId }
when { when {
student != null -> student student != null -> student
studentId != 0L && studentRepository.isCurrentStudentSet() -> { studentId != 0L && studentRepository.isCurrentStudentSet() -> {
@ -263,7 +274,10 @@ class TimetableWidgetProvider : BroadcastReceiver() {
} }
private fun setupAccountView( private fun setupAccountView(
context: Context, student: Student, remoteViews: RemoteViews, widgetId: Int context: Context,
student: Student,
remoteViews: RemoteViews,
widgetId: Int
) { ) {
val accountInitials = getAccountInitials(student.nickOrName) val accountInitials = getAccountInitials(student.nickOrName)
val accountPickerPendingIntent = createAccountPickerPendingIntent(context, widgetId) val accountPickerPendingIntent = createAccountPickerPendingIntent(context, widgetId)

View File

@ -37,7 +37,7 @@ class StudentDaoTest {
} }
@Test @Test
fun `get students associated with correct semester`() = runTest { fun `get students associated with correct semester with same studentId`() = runTest {
val notEduOneStudent = getStudentEntity() val notEduOneStudent = getStudentEntity()
.copy( .copy(
isEduOne = false, isEduOne = false,
@ -96,6 +96,64 @@ class StudentDaoTest {
) )
} }
@Test
fun `get students associated with correct semester with different studentId`() = runTest {
val notEduOneStudent = getStudentEntity()
.copy(
isEduOne = false,
classId = 42,
studentId = 100
)
.apply { id = 1 }
val eduOneStudent = getStudentEntity()
.copy(
isEduOne = true,
classId = 0,
studentId = 101
)
.apply { id = 2 }
val semesterAssociatedWithNotEduOneStudent = getSemesterEntity()
.copy(
studentId = notEduOneStudent.studentId,
classId = notEduOneStudent.classId,
)
.apply { id = 0 }
val semesterAssociatedWithEduOneStudent = getSemesterEntity()
.copy(
studentId = eduOneStudent.studentId,
classId = eduOneStudent.classId,
)
.apply { id = 0 }
studentDao.insertAll(listOf(notEduOneStudent, eduOneStudent))
semesterDao.insertAll(
listOf(
semesterAssociatedWithNotEduOneStudent,
semesterAssociatedWithEduOneStudent
)
)
val studentsWithSemesters = studentDao.loadStudentsWithSemesters()
val notEduOneSemestersResult = studentsWithSemesters.entries
.find { (student, _) -> student.id == notEduOneStudent.id }
?.value
val eduOneSemestersResult = studentsWithSemesters.entries
.find { (student, _) -> student.id == eduOneStudent.id }
?.value
assertEquals(2, studentsWithSemesters.size)
assertEquals(1, notEduOneSemestersResult?.size)
assertEquals(1, eduOneSemestersResult?.size)
assertEquals(semesterAssociatedWithEduOneStudent, eduOneSemestersResult?.firstOrNull())
assertEquals(
semesterAssociatedWithNotEduOneStudent,
notEduOneSemestersResult?.firstOrNull()
)
}
@After @After
fun closeDb() { fun closeDb() {
db.close() db.close()

View File

@ -12,9 +12,7 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import kotlin.random.Random import kotlin.random.Random
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertNull import kotlin.test.assertNull
import kotlin.test.assertTrue
@HiltAndroidTest @HiltAndroidTest
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@ -22,9 +20,10 @@ import kotlin.test.assertTrue
class Migration63Test : AbstractMigrationTest() { class Migration63Test : AbstractMigrationTest() {
@Test @Test
fun `update is_edu_one to null if 0`() = runTest { fun `update is_edu_one to null`() = runTest {
with(helper.createDatabase(dbName, 62)) { with(helper.createDatabase(dbName, 62)) {
createStudent(1, 0) createStudent(1, 0)
createStudent(2, 1)
close() close()
} }
@ -32,31 +31,15 @@ class Migration63Test : AbstractMigrationTest() {
val database = getMigratedRoomDatabase() val database = getMigratedRoomDatabase()
val studentDb = database.studentDao val studentDb = database.studentDao
val student = studentDb.loadById(1) val student1 = studentDb.loadById(1)
val student2 = studentDb.loadById(2)
assertNull(student!!.isEduOne) assertNull(student1!!.isEduOne)
assertNull(student2!!.isEduOne)
database.close() database.close()
} }
@Test
fun `check is_edu_one is stay same`() = runTest {
with(helper.createDatabase(dbName, 62)) {
createStudent(1, 1)
close()
}
helper.runMigrationsAndValidate(dbName, 63, true)
val database = getMigratedRoomDatabase()
val studentDb = database.studentDao
val student = studentDb.loadById(1)
val isEduOne = assertNotNull(student!!.isEduOne)
assertTrue(isEduOne)
database.close()
}
private fun SupportSQLiteDatabase.createStudent(id: Long, isEduOneValue: Int) { private fun SupportSQLiteDatabase.createStudent(id: Long, isEduOneValue: Int) {
insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
put("scrapper_base_url", "https://fakelog.cf") put("scrapper_base_url", "https://fakelog.cf")

View File

@ -76,7 +76,9 @@ class SemesterRepositoryTest {
getSemesterPojo(123, 2, now().minusMonths(3), now()) getSemesterPojo(123, 2, now().minusMonths(3), now())
) )
coEvery { semesterDb.loadAll(student) } returns badSemesters.mapToEntities(student.studentId) coEvery {
semesterDb.loadAll(student)
} returns badSemesters.mapToEntities(student.studentId)
coEvery { sdk.getSemesters() } returns goodSemesters coEvery { sdk.getSemesters() } returns goodSemesters
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
@ -188,7 +190,9 @@ class SemesterRepositoryTest {
getSemesterPojo(2, 2, now().plusMonths(5), now().plusMonths(11)), getSemesterPojo(2, 2, now().plusMonths(5), now().plusMonths(11)),
) )
coEvery { semesterDb.loadAll(student) } returns semestersWithNoCurrent coEvery {
semesterDb.loadAll(student)
} returns semestersWithNoCurrent
coEvery { sdk.getSemesters() } returns newSemesters coEvery { sdk.getSemesters() } returns newSemesters
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs