Automatically switch semesters without sync (#681)

This commit is contained in:
Mikołaj Pich
2020-02-24 00:24:40 +01:00
committed by GitHub
parent 00f5b9431e
commit 96c1bb4c69
20 changed files with 246 additions and 153 deletions

View File

@ -27,9 +27,6 @@ data class Semester(
@ColumnInfo(name = "semester_name")
val semesterName: Int,
@ColumnInfo(name = "is_current")
val isCurrent: Boolean,
val start: LocalDate,
val end: LocalDate,
@ -43,4 +40,8 @@ data class Semester(
@PrimaryKey(autoGenerate = true)
var id: Long = 0
@ColumnInfo(name = "is_current")
var current: Boolean = false
}

View File

@ -19,6 +19,6 @@ class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
}
fun getSemesters(student: Student): Maybe<List<Semester>> {
return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() }
return semesterDb.loadAll(student.studentId, student.classId).filter { it.isNotEmpty() }
}
}

View File

@ -20,7 +20,6 @@ class SemesterRemote @Inject constructor(private val sdk: Sdk) {
schoolYear = it.schoolYear,
semesterId = it.semesterId,
semesterName = it.semesterNumber,
isCurrent = it.current,
start = it.start,
end = it.end,
classId = it.classId,

View File

@ -5,10 +5,11 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.getCurrentOrLast
import io.github.wulkanowy.utils.isCurrent
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Maybe
import io.reactivex.Single
import timber.log.Timber
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@ -21,28 +22,30 @@ class SemesterRepository @Inject constructor(
private val sdkHelper: SdkHelper
) {
fun getSemesters(student: Student, forceRefresh: Boolean = false): Single<List<Semester>> {
fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): Single<List<Semester>> {
return Maybe.just(sdkHelper.init(student))
.flatMap { local.getSemesters(student).filter { !forceRefresh } }
.flatMap {
local.getSemesters(student).filter { !forceRefresh }.filter {
if (refreshOnNoCurrent) {
it.any { semester -> semester.isCurrent }
} else true
}
}
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getSemesters(student) else Single.error(UnknownHostException())
if (it) remote.getSemesters(student)
else Single.error(UnknownHostException())
}.flatMap { new ->
val currentSemesters = new.filter { it.isCurrent }
if (currentSemesters.size == 1) {
local.getSemesters(student).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteSemesters(old.uniqueSubtract(new))
local.saveSemesters(new.uniqueSubtract(old))
}
} else {
Timber.i("Current semesters list:\n${new.joinToString(separator = "\n")}")
throw IllegalArgumentException("Current semester can be only one.")
if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!")
local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old ->
local.deleteSemesters(old.uniqueSubtract(new))
local.saveSemesters(new.uniqueSubtract(old))
}
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
}
fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single<Semester> {
return getSemesters(student, forceRefresh).map { item -> item.single { it.isCurrent } }
return getSemesters(student, forceRefresh).map { it.getCurrentOrLast() }
}
}

View File

@ -7,7 +7,9 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.getCurrentOrLast
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class GradePresenter @Inject constructor(
@ -98,17 +100,16 @@ class GradePresenter @Inject constructor(
private fun loadData() {
Timber.i("Loading grade data started")
disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it) }
.flatMap { semesterRepository.getSemesters(it, refreshOnNoCurrent = true) }
.delay(200, MILLISECONDS)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.showProgress(false) }
.subscribe({
it.first { item -> item.isCurrent }.also { current ->
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
schoolYear = current.schoolYear
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
view?.setCurrentSemesterName(current.semesterName, schoolYear)
}
val current = it.getCurrentOrLast()
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
schoolYear = current.schoolYear
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
view?.setCurrentSemesterName(current.semesterName, schoolYear)
view?.run {
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
@ -125,6 +126,7 @@ class GradePresenter @Inject constructor(
private fun showErrorViewOnError(message: String, error: Throwable) {
lastError = error
view?.run {
showProgress(false)
showErrorView(true)
setErrorDetails(message)
}

View File

@ -0,0 +1,19 @@
package io.github.wulkanowy.utils
import io.github.wulkanowy.data.db.entities.Semester
import org.threeten.bp.LocalDate.now
inline val Semester.isCurrent: Boolean
get() = now() in start..end
fun List<Semester>.getCurrentOrLast(): Semester {
if (isEmpty()) throw RuntimeException("Empty semester list")
// when there is only one current semester
singleOrNull { it.isCurrent }?.let { return it }
// when there is more than one current semester - find one with higher id
singleOrNull { semester -> semester.semesterId == maxBy { it.semesterId }?.semesterId }?.let { return it }
throw IllegalArgumentException("Duplicated last semester! Semesters: ${joinToString(separator = "\n")}")
}

View File

@ -1,42 +1,30 @@
package io.github.wulkanowy.utils
import org.threeten.bp.DateTimeUtils
import org.threeten.bp.DayOfWeek.FRIDAY
import org.threeten.bp.DayOfWeek.MONDAY
import org.threeten.bp.DayOfWeek.SATURDAY
import org.threeten.bp.DayOfWeek.SUNDAY
import org.threeten.bp.Instant
import org.threeten.bp.Instant.ofEpochMilli
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDateTime
import org.threeten.bp.Month
import org.threeten.bp.ZoneId
import org.threeten.bp.format.DateTimeFormatter.ofPattern
import org.threeten.bp.format.TextStyle.FULL_STANDALONE
import org.threeten.bp.temporal.TemporalAdjusters.firstInMonth
import org.threeten.bp.temporal.TemporalAdjusters.next
import org.threeten.bp.temporal.TemporalAdjusters.previous
import java.util.Date
import java.util.Locale
private const val DATE_PATTERN = "dd.MM.yyyy"
fun Date.toLocalDate(): LocalDate = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()).toLocalDate()
fun Date.toLocalDateTime(): LocalDateTime = ofEpochMilli(time).atZone(ZoneId.systemDefault()).toLocalDateTime()
fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate = LocalDate.parse(this, ofPattern(format))
fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = format(ofPattern(format))
fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = format(ofPattern(format))
fun LocalDateTime.toDate(): Date = DateTimeUtils.toDate(atZone(ZoneId.systemDefault()).toInstant())
/**
* https://github.com/ThreeTen/threetenbp/issues/55
*/
fun Month.getFormattedName(): String {
return getDisplayName(FULL_STANDALONE, Locale.getDefault())
.let {
@ -93,9 +81,6 @@ inline val LocalDate.previousOrSameSchoolDay: LocalDate
inline val LocalDate.weekDayName: String
get() = format(ofPattern("EEEE", Locale.getDefault()))
inline val LocalDate.shortcutWeekDayName: String
get() = format(ofPattern("EEE", Locale.getDefault()))
inline val LocalDate.monday: LocalDate
get() = with(MONDAY)
@ -105,7 +90,6 @@ inline val LocalDate.friday: LocalDate
/**
* [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335)
*/
inline val LocalDate.isHolidays: Boolean
get() = isBefore(firstSchoolDay) && isAfter(lastSchoolDay)
@ -121,7 +105,6 @@ inline val LocalDate.lastSchoolDay: LocalDate
get() = LocalDate.of(year, 6, 20)
.with(next(FRIDAY))
private fun Int.getSchoolYearByMonth(monthValue: Int): Int {
return when (monthValue) {
in 9..12 -> this