Display day header from website in timetable (#1269)

This commit is contained in:
Mikołaj Pich 2021-04-05 15:07:29 +02:00 committed by GitHub
parent 7bc5219d81
commit aeb3b2a030
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2497 additions and 71 deletions

View File

@ -160,7 +160,7 @@ ext {
}
dependencies {
implementation "io.github.wulkanowy:sdk:1.1.3"
implementation "io.github.wulkanowy:sdk:877b9135"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'

File diff suppressed because it is too large Load Diff

View File

@ -181,4 +181,8 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideStudentInfoDao(database: AppDatabase) = database.studentInfoDao
@Singleton
@Provides
fun provideTimetableHeaderDao(database: AppDatabase) = database.timetableHeaderDao
}

View File

@ -32,6 +32,7 @@ import io.github.wulkanowy.data.db.dao.SubjectDao
import io.github.wulkanowy.data.db.dao.TeacherDao
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.CompletedLesson
@ -58,6 +59,7 @@ import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.db.migrations.Migration10
import io.github.wulkanowy.data.db.migrations.Migration11
import io.github.wulkanowy.data.db.migrations.Migration12
@ -87,6 +89,7 @@ import io.github.wulkanowy.data.db.migrations.Migration33
import io.github.wulkanowy.data.db.migrations.Migration34
import io.github.wulkanowy.data.db.migrations.Migration35
import io.github.wulkanowy.data.db.migrations.Migration36
import io.github.wulkanowy.data.db.migrations.Migration37
import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration5
import io.github.wulkanowy.data.db.migrations.Migration6
@ -125,6 +128,7 @@ import javax.inject.Singleton
Conference::class,
TimetableAdditional::class,
StudentInfo::class,
TimetableHeader::class,
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@ -133,7 +137,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 36
const val VERSION_SCHEMA = 37
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@ -170,7 +174,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration33(),
Migration34(),
Migration35(appInfo),
Migration36()
Migration36(),
Migration37(),
)
fun newInstance(
@ -236,4 +241,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val timetableAdditionalDao: TimetableAdditionalDao
abstract val studentInfoDao: StudentInfoDao
abstract val timetableHeaderDao: TimetableHeaderDao
}

View File

@ -0,0 +1,16 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.TimetableHeader
import kotlinx.coroutines.flow.Flow
import java.time.LocalDate
import javax.inject.Singleton
@Dao
@Singleton
interface TimetableHeaderDao : BaseDao<TimetableHeader> {
@Query("SELECT * FROM TimetableHeaders WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<TimetableHeader>>
}

View File

@ -0,0 +1,25 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "TimetableHeaders")
data class TimetableHeader(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "diary_id")
val diaryId: Int,
val date: LocalDate,
val content: String,
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,21 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration37 : Migration(36, 37) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS TimetableHeaders (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
diary_id INTEGER NOT NULL,
date INTEGER NOT NULL,
content TEXT NOT NULL
)
"""
)
}
}

View File

@ -3,9 +3,19 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.pojos.TimetableFull
import io.github.wulkanowy.sdk.pojo.TimetableFull as SdkTimetableFull
import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader
import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable
import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional
fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
lessons = lessons.mapToEntities(semester),
additional = additional.mapToEntities(semester),
headers = headers.mapToEntities(semester)
)
fun List<SdkTimetable>.mapToEntities(semester: Semester) = map {
Timetable(
studentId = semester.studentId,
@ -39,3 +49,13 @@ fun List<SdkTimetableAdditional>.mapToEntities(semester: Semester) = map {
end = it.end
)
}
@JvmName("mapToEntitiesTimetableHeaders")
fun List<SdkTimetableHeader>.mapToEntities(semester: Semester) = map {
TimetableHeader(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date,
content = it.content
)
}

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.data.pojos
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader
data class TimetableFull(
val lessons: List<Timetable>,
val additional: List<TimetableAdditional>,
val headers: List<TimetableHeader>,
)

View File

@ -2,11 +2,14 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.pojos.TimetableFull
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.utils.AutoRefreshHelper
@ -16,8 +19,8 @@ import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
@ -27,6 +30,7 @@ import javax.inject.Singleton
class TimetableRepository @Inject constructor(
private val timetableDb: TimetableDao,
private val timetableAdditionalDb: TimetableAdditionalDao,
private val timetableHeaderDb: TimetableHeaderDao,
private val sdk: Sdk,
private val schedulerHelper: TimetableNotificationSchedulerHelper,
private val refreshHelper: AutoRefreshHelper,
@ -36,53 +40,111 @@ class TimetableRepository @Inject constructor(
private val cacheKey = "timetable"
fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean, refreshAdditional: Boolean = false) = networkBoundResource(
fun getTimetable(
student: Student, semester: Semester, start: LocalDate, end: LocalDate,
forceRefresh: Boolean, refreshAdditional: Boolean = false
) = networkBoundResource(
mutex = saveFetchResultMutex,
shouldFetch = { (timetable, additional) -> timetable.isEmpty() || (additional.isEmpty() && refreshAdditional) || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = {
timetableDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
.map { schedulerHelper.scheduleNotifications(it, student); it }
.combine(timetableAdditionalDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)) { timetable, additional ->
timetable to additional
}
},
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getTimetable(start.monday, end.sunday)
.let { (normal, additional) -> normal.mapToEntities(semester) to additional.mapToEntities(semester) }
shouldFetch = { (timetable, additional, headers) ->
val refreshKey = getRefreshKey(cacheKey, semester, start, end)
val isShouldRefresh = refreshHelper.isShouldBeRefreshed(refreshKey)
val isRefreshAdditional = additional.isEmpty() && refreshAdditional
val isNoData = timetable.isEmpty() || isRefreshAdditional || headers.isEmpty()
isNoData || forceRefresh || isShouldRefresh
},
saveFetchResult = { (oldTimetable, oldAdditional), (newTimetable, newAdditional) ->
refreshTimetable(student, oldTimetable, newTimetable)
refreshAdditional(oldAdditional, newAdditional)
query = { getFullTimetableFromDatabase(student, semester, start, end) },
fetch = {
val timetableFull = sdk.init(student)
.switchDiary(semester.diaryId, semester.schoolYear)
.getTimetableFull(start.monday, end.sunday)
timetableFull.mapToEntities(semester)
},
saveFetchResult = { timetableOld, timetableNew ->
refreshTimetable(student, timetableOld.lessons, timetableNew.lessons)
refreshAdditional(timetableOld.additional, timetableNew.additional)
refreshDayHeaders(timetableOld.headers, timetableNew.headers)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { (timetable, additional) ->
timetable.filter { item ->
item.date in start..end
} to additional.filter { item ->
item.date in start..end
}
filterResult = { (timetable, additional, headers) ->
TimetableFull(
lessons = timetable.filter { it.date in start..end },
additional = additional.filter { it.date in start..end },
headers = headers.filter { it.date in start..end }
)
}
)
private suspend fun refreshTimetable(student: Student, old: List<Timetable>, new: List<Timetable>) {
timetableDb.deleteAll(old.uniqueSubtract(new).also { schedulerHelper.cancelScheduled(it) })
timetableDb.insertAll(new.uniqueSubtract(old).also { schedulerHelper.scheduleNotifications(it, student) }.map { item ->
item.also { new ->
old.singleOrNull { new.start == it.start }?.let { old ->
return@map new.copy(
room = if (new.room.isEmpty()) old.room else new.room,
teacher = if (new.teacher.isEmpty() && !new.changes && !old.changes) old.teacher else new.teacher
)
}
}
})
private fun getFullTimetableFromDatabase(
student: Student, semester: Semester,
start: LocalDate, end: LocalDate
): Flow<TimetableFull> {
val timetableFlow = timetableDb.loadAll(
diaryId = semester.diaryId,
studentId = semester.studentId,
from = start.monday,
end = end.sunday
)
val headersFlow = timetableHeaderDb.loadAll(
diaryId = semester.diaryId,
studentId = semester.studentId,
from = start.monday,
end = end.sunday
)
val additionalFlow = timetableAdditionalDb.loadAll(
diaryId = semester.diaryId,
studentId = semester.studentId,
from = start.monday,
end = end.sunday
)
return combine(timetableFlow, headersFlow, additionalFlow) { lessons, headers, additional ->
schedulerHelper.scheduleNotifications(lessons, student)
TimetableFull(
lessons = lessons,
headers = headers,
additional = additional
)
}
}
private suspend fun refreshAdditional(old: List<TimetableAdditional>, new: List<TimetableAdditional>) {
timetableAdditionalDb.deleteAll(old.uniqueSubtract(new))
timetableAdditionalDb.insertAll(new.uniqueSubtract(old))
private suspend fun refreshTimetable(
student: Student,
lessonsOld: List<Timetable>, lessonsNew: List<Timetable>
) {
val lessonsToRemove = lessonsOld uniqueSubtract lessonsNew
val lessonsToAdd = (lessonsNew uniqueSubtract lessonsOld).map { new ->
val matchingOld = lessonsOld.singleOrNull { new.start == it.start }
if (matchingOld != null) {
val useOldTeacher = new.teacher.isEmpty() && !new.changes && !matchingOld.changes
new.copy(
room = if (new.room.isEmpty()) matchingOld.room else new.room,
teacher = if (useOldTeacher) matchingOld.teacher
else new.teacher
)
} else new
}
timetableDb.deleteAll(lessonsToRemove)
timetableDb.insertAll(lessonsToAdd)
schedulerHelper.cancelScheduled(lessonsToRemove, student)
schedulerHelper.scheduleNotifications(lessonsToAdd, student)
}
private suspend fun refreshAdditional(
old: List<TimetableAdditional>,
new: List<TimetableAdditional>
) {
timetableAdditionalDb.deleteAll(old uniqueSubtract new)
timetableAdditionalDb.insertAll(new uniqueSubtract old)
}
private suspend fun refreshDayHeaders(old: List<TimetableHeader>, new: List<TimetableHeader>) {
timetableHeaderDb.deleteAll(old uniqueSubtract new)
timetableHeaderDb.insertAll(new uniqueSubtract old)
}
}

View File

@ -51,7 +51,8 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
lesson: Timetable
) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
suspend fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
suspend fun cancelScheduled(lessons: List<Timetable>, student: Student) {
val studentId = student.studentId
withContext(dispatchersProvider.backgroundThread) {
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
@ -78,7 +79,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
suspend fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) {
return cancelScheduled(lessons, student.studentId)
return cancelScheduled(lessons, student)
}
withContext(dispatchersProvider.backgroundThread) {
@ -89,7 +90,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
val canceled = day.filter { it.canceled }
val active = day.filter { !it.canceled }
cancelScheduled(canceled)
cancelScheduled(canceled, student)
active.forEachIndexed { index, lesson ->
val intent = createIntent(student, lesson, active.getOrNull(index + 1))

View File

@ -7,6 +7,7 @@ import android.view.MenuItem
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.core.text.HtmlCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
import dagger.hilt.android.AndroidEntryPoint
@ -71,7 +72,9 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
with(binding) {
timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
timetableSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
timetableSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
timetableSwipe.setProgressBackgroundColorSchemeColor(
requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)
)
timetableErrorRetry.setOnClickListener { presenter.onRetry() }
timetableErrorDetails.setOnClickListener { presenter.onDetailsClick() }
@ -95,7 +98,12 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
}
}
override fun updateData(data: List<Timetable>, showWholeClassPlanType: String, showGroupsInPlanType: Boolean, showTimetableTimers: Boolean) {
override fun updateData(
data: List<Timetable>,
showWholeClassPlanType: String,
showGroupsInPlanType: Boolean,
showTimetableTimers: Boolean
) {
with(timetableAdapter) {
items = data.toMutableList()
showTimers = showTimetableTimers
@ -136,6 +144,13 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
binding.timetableEmpty.visibility = if (show) VISIBLE else GONE
}
override fun setDayHeaderMessage(message: String?) {
binding.timetableEmptyMessage.visibility = if (message.isNullOrEmpty()) GONE else VISIBLE
binding.timetableEmptyMessage.text = HtmlCompat.fromHtml(
message.orEmpty(), HtmlCompat.FROM_HTML_MODE_COMPACT
)
}
override fun showErrorView(show: Boolean) {
binding.timetableError.visibility = if (show) VISIBLE else GONE
}
@ -172,8 +187,10 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
presenter.onDateSet(year, month + 1, dayOfMonth)
}
val datePickerDialog = DatePickerDialog.newInstance(dateSetListener,
currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth)
val datePickerDialog = DatePickerDialog.newInstance(
dateSetListener,
currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth
)
with(datePickerDialog) {
setDateRangeLimiter(SchooldaysRangeLimiter())

View File

@ -138,32 +138,37 @@ class TimetablePresenter @Inject constructor(
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
timetableRepository.getTimetable(student, semester, currentDate, currentDate, forceRefresh)
timetableRepository.getTimetable(
student, semester, currentDate, currentDate, forceRefresh
)
}.onEach {
when (it.status) {
Status.LOADING -> {
if (!it.data?.first.isNullOrEmpty()) {
if (!it.data?.lessons.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(it.data!!.first)
updateData(it.data!!.lessons)
}
}
}
Status.SUCCESS -> {
Timber.i("Loading timetable result: Success")
view?.apply {
updateData(it.data!!.first)
showEmpty(it.data.first.isEmpty())
updateData(it.data!!.lessons)
showEmpty(it.data.lessons.isEmpty())
setDayHeaderMessage(it.data.headers.singleOrNull { header ->
header.date == currentDate
}?.content)
showErrorView(false)
showContent(it.data.first.isNotEmpty())
showContent(it.data.lessons.isNotEmpty())
}
analytics.logEvent(
"load_data",
"type" to "timetable",
"items" to it.data!!.first.size
"items" to it.data!!.lessons.size
)
}
Status.ERROR -> {

View File

@ -24,6 +24,8 @@ interface TimetableView : BaseView {
fun showEmpty(show: Boolean)
fun setDayHeaderMessage(message: String?)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)

View File

@ -110,15 +110,15 @@ class AdditionalLessonsPresenter @Inject constructor(
Status.SUCCESS -> {
Timber.i("Loading additional lessons lessons result: Success")
view?.apply {
updateData(it.data!!.second.sortedBy { item -> item.date })
showEmpty(it.data.second.isEmpty())
updateData(it.data!!.additional.sortedBy { item -> item.date })
showEmpty(it.data.additional.isEmpty())
showErrorView(false)
showContent(it.data.second.isNotEmpty())
showContent(it.data.additional.isNotEmpty())
}
analytics.logEvent(
"load_data",
"type" to "additional_lessons",
"items" to it.data!!.second.size
"items" to it.data!!.additional.size
)
}
Status.ERROR -> {

View File

@ -107,7 +107,7 @@ class TimetableWidgetFactory(
val semester = semesterRepository.getCurrentSemester(student)
timetableRepository.getTimetable(student, semester, date, date, false)
.toFirstResult().data?.first.orEmpty()
.toFirstResult().data?.lessons.orEmpty()
.sortedWith(compareBy({ it.number }, { !it.isStudentPlan }))
.filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true }
}

View File

@ -15,7 +15,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" />
android:indeterminate="true"
tools:visibility="gone" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/timetableSwipe"
@ -26,7 +27,8 @@
android:id="@+id/timetableRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_timetable" />
tools:listitem="@layout/item_timetable"
tools:visibility="visible" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
@ -53,6 +55,18 @@
android:gravity="center"
android:text="@string/timetable_no_items"
android:textSize="20sp" />
<TextView
android:id="@+id/timetableEmptyMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:gravity="center"
android:lineSpacingMultiplier="1.2"
android:textColor="?android:textColorSecondary"
android:textSize="16sp"
tools:maxLines="4"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>
<LinearLayout

View File

@ -121,7 +121,7 @@
app:layout_constraintStart_toEndOf="@+id/timetableItemTeacher"
app:layout_constraintTop_toTopOf="@+id/timetableItemTimeFinish"
tools:text="Lekcja odwołana: uczniowie zwolnieni do domu"
tools:visibility="visible" />
tools:visibility="gone" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/timetableItemTimeBarrier"

View File

@ -2,10 +2,12 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.TimetableFull
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.toFirstResult
@ -41,6 +43,9 @@ class TimetableRepositoryTest {
@MockK
private lateinit var timetableAdditionalDao: TimetableAdditionalDao
@MockK
private lateinit var timetableHeaderDao: TimetableHeaderDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
@ -59,7 +64,7 @@ class TimetableRepositoryTest {
MockKAnnotations.init(this)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
timetableRepository = TimetableRepository(timetableDb, timetableAdditionalDao, sdk, timetableNotificationSchedulerHelper, refreshHelper)
timetableRepository = TimetableRepository(timetableDb, timetableAdditionalDao, timetableHeaderDao, sdk, timetableNotificationSchedulerHelper, refreshHelper)
}
@Test
@ -71,7 +76,7 @@ class TimetableRepositoryTest {
createTimetableRemote(of(2021, 1, 4, 9, 40), 3, "", "W-F"),
createTimetableRemote(of(2021, 1, 4, 10, 30), 4, "", "W-F")
)
coEvery { sdk.getTimetable(any(), any()) } returns (remoteList to emptyList())
coEvery { sdk.getTimetableFull(any(), any()) } returns TimetableFull(emptyList(), remoteList, emptyList())
val localList = listOf(
createTimetableRemote(of(2021, 1, 4, 8, 0), 1, "123", "Przyroda"),
@ -87,13 +92,17 @@ class TimetableRepositoryTest {
coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
coEvery { timetableHeaderDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
coEvery { timetableHeaderDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
// execute
val res = runBlocking {
timetableRepository.getTimetable(student, semester, startDate, endDate, true).toFirstResult()
}
// verify
assertEquals(4, res.data?.first.orEmpty().size)
assertEquals(4, res.data?.lessons.orEmpty().size)
coVerify {
timetableDb.insertAll(withArg {
assertEquals(4, it.size)
@ -124,7 +133,7 @@ class TimetableRepositoryTest {
createTimetableRemote(of(2021, 1, 6, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2021, 1, 6, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
)
coEvery { sdk.getTimetable(startDate, endDate) } returns (remoteList to emptyList())
coEvery { sdk.getTimetableFull(startDate, endDate) } returns TimetableFull(emptyList(), remoteList, emptyList())
val localList = listOf(
createTimetableRemote(of(2021, 1, 4, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
@ -150,12 +159,16 @@ class TimetableRepositoryTest {
coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
coEvery { timetableHeaderDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
coEvery { timetableHeaderDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
// execute
val res = runBlocking { timetableRepository.getTimetable(student, semester, startDate, endDate, true).toFirstResult() }
// verify
assertEquals(null, res.error)
assertEquals(12, res.data!!.first.size)
assertEquals(12, res.data!!.lessons.size)
coVerify {
timetableDb.insertAll(withArg {
@ -187,7 +200,7 @@ class TimetableRepositoryTest {
)
// prepare
coEvery { sdk.getTimetable(startDate, endDate) } returns (remoteList to emptyList())
coEvery { sdk.getTimetableFull(startDate, endDate) } returns TimetableFull(emptyList(), remoteList, emptyList())
coEvery { timetableDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
@ -199,13 +212,17 @@ class TimetableRepositoryTest {
coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
coEvery { timetableHeaderDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
coEvery { timetableHeaderDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
// execute
val res = runBlocking { timetableRepository.getTimetable(student, semester, startDate, endDate, true).toFirstResult() }
// verify
assertEquals(null, res.error)
assertEquals(2, res.data?.first?.size)
coVerify { sdk.getTimetable(startDate, endDate) }
assertEquals(2, res.data?.lessons?.size)
coVerify { sdk.getTimetableFull(startDate, endDate) }
coVerify { timetableDb.loadAll(1, 1, startDate, endDate) }
coVerify { timetableDb.insertAll(match { it.isEmpty() }) }
coVerify { timetableDb.deleteAll(match { it.isEmpty() }) }