1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2025-01-31 15:18:20 +01:00

Migrate presenters from rxjava to coroutines flow (#894)

This commit is contained in:
Mikołaj Pich 2020-07-19 13:30:29 +02:00 committed by GitHub
parent b0a674b471
commit 1ac42bb56d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
138 changed files with 2351 additions and 1892 deletions

View File

@ -92,6 +92,7 @@ android {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
packagingOptions {
@ -116,7 +117,7 @@ ext {
room = "2.2.5"
dagger = "2.28.3"
chucker = "3.2.0"
mockk = "1.9.2"
mockk = "1.10.0"
}
configurations.all {
@ -129,6 +130,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.3.7'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7'
implementation "androidx.core:core-ktx:1.3.0"
implementation "androidx.activity:activity-ktx:1.1.0"
@ -156,7 +158,6 @@ dependencies {
implementation 'com.github.PaulinaSadowska:RxWorkManagerObservers:1.0.0'
implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-rxjava2:$room"
implementation "androidx.room:room-ktx:$room"
kapt "androidx.room:room-compiler:$room"
@ -199,6 +200,7 @@ dependencies {
testImplementation "junit:junit:4.13"
testImplementation "io.mockk:mockk:$mockk"
testImplementation "org.threeten:threetenbp:1.4.4"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.7'
androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0"

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.data
import io.github.wulkanowy.utils.DispatchersProvider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
class TestDispatchersProvider : DispatchersProvider() {
override val backgroundThread: CoroutineDispatcher
get() = Dispatchers.Unconfined
}

View File

@ -1,6 +1,8 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDateTime
fun getStudent(): Student {
@ -27,3 +29,16 @@ fun getStudent(): Student {
isParent = false
)
}
fun getSemester() = Semester(
semesterId = 1,
studentId = 1,
classId = 1,
diaryId = 2,
diaryName = "",
end = now(),
schoolYear = 2019,
semesterName = 1,
start = now(),
unitId = 1
)

View File

@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -53,7 +54,7 @@ class AttendanceLocalTest {
runBlocking { attendanceLocal.saveAttendance(list) }
val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
val attendance = runBlocking { attendanceLocal.getAttendance(semester, of(2018, 9, 10), of(2018, 9, 14)) }
val attendance = runBlocking { attendanceLocal.getAttendance(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
assertEquals(2, attendance.size)
assertEquals(attendance[0].date, of(2018, 9, 10))
assertEquals(attendance[1].date, of(2018, 9, 14))

View File

@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -46,7 +47,7 @@ class CompletedLessonsLocalTest {
runBlocking { completedLessonsLocal.saveCompletedLessons(list) }
val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
val completed = runBlocking { completedLessonsLocal.getCompletedLessons(semester, of(2018, 9, 10), of(2018, 9, 14)) }
val completed = runBlocking { completedLessonsLocal.getCompletedLessons(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
assertEquals(2, completed.size)
assertEquals(completed[0].date, of(2018, 9, 10))
assertEquals(completed[1].date, of(2018, 9, 14))

View File

@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -43,7 +44,7 @@ class ExamLocalTest {
runBlocking { examLocal.saveExams(list) }
val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
val exams = runBlocking { examLocal.getExams(semester, of(2018, 9, 10), of(2018, 9, 14)) }
val exams = runBlocking { examLocal.getExams(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
assertEquals(2, exams.size)
assertEquals(exams[0].date, of(2018, 9, 10))
assertEquals(exams[1].date, of(2018, 9, 14))

View File

@ -5,6 +5,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -45,7 +46,7 @@ class GradeLocalTest {
val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1)
val grades = runBlocking { gradeLocal.getGradesDetails(semester) }
val grades = runBlocking { gradeLocal.getGradesDetails(semester).first() }
assertEquals(2, grades.size)
assertEquals(grades[0].date, LocalDate.of(2019, 2, 27))

View File

@ -5,15 +5,18 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Grade
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -29,15 +32,12 @@ import kotlin.test.assertTrue
@RunWith(AndroidJUnit4::class)
class GradeRepositoryTest {
@MockK
private lateinit var mockSdk: Sdk
@MockK
private lateinit var semesterMock: Semester
@MockK
private lateinit var studentMock: Student
@MockK
private lateinit var gradeRemote: GradeRemote
private lateinit var gradeLocal: GradeLocal
@ -49,14 +49,12 @@ class GradeRepositoryTest {
MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao)
gradeRemote = GradeRemote(mockSdk)
studentMock = getStudentMock()
every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0)
every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
}
@After
@ -66,16 +64,17 @@ class GradeRepositoryTest {
@Test
fun markOlderThanRegisterDateAsRead() {
coEvery { mockSdk.getGrades(1) } returns (listOf(
createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"),
createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"),
createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeLocal(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"),
createGradeLocal(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
createGradeLocal(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"),
createGradeLocal(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
) to emptyList())
val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true)
.first.sortedByDescending { it.date }
GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!.first.sortedByDescending { it.date }
}
assertFalse { grades[0].isRead }
@ -93,16 +92,17 @@ class GradeRepositoryTest {
)
runBlocking { gradeLocal.saveGrades(list) }
coEvery { mockSdk.getGrades(1) } returns (listOf(
createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeLocal(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
createGradeLocal(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
createGradeLocal(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
createGradeLocal(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
) to emptyList())
val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true)
.first.sortedByDescending { it.date }
GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!.first.sortedByDescending { it.date }
}
assertFalse { grades[0].isRead }
@ -120,13 +120,15 @@ class GradeRepositoryTest {
)
runBlocking { gradeLocal.saveGrades(list) }
coEvery { mockSdk.getGrades(1) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList())
val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true)
GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
}
assertEquals(2, grades.first.size)
@ -140,14 +142,16 @@ class GradeRepositoryTest {
)
runBlocking { gradeLocal.saveGrades(list) }
coEvery { mockSdk.getGrades(1) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList())
val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true)
GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
}
assertEquals(3, grades.first.size)
@ -157,14 +161,16 @@ class GradeRepositoryTest {
fun emptyLocal() {
runBlocking { gradeLocal.saveGrades(listOf()) }
coEvery { mockSdk.getGrades(1) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList())
val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true)
GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
}
assertEquals(3, grades.first.size)
@ -178,12 +184,37 @@ class GradeRepositoryTest {
)
runBlocking { gradeLocal.saveGrades(list) }
coEvery { mockSdk.getGrades(1) } returns (emptyList<Grade>() to emptyList())
coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (emptyList<Grade>() to emptyList())
val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true)
GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
}
assertEquals(0, grades.first.size)
}
private fun getStudentMock() = Student(
scrapperBaseUrl = "http://fakelog.cf",
email = "jan@fakelog.cf",
certificateKey = "",
classId = 0,
className = "",
isCurrent = false,
isParent = false,
loginMode = Sdk.Mode.SCRAPPER.name,
loginType = "STANDARD",
mobileBaseUrl = "",
password = "",
privateKey = "",
registrationDate = LocalDateTime.of(2019, 2, 27, 12, 0),
schoolName = "",
schoolShortName = "test",
schoolSymbol = "",
studentId = 0,
studentName = "",
symbol = "",
userLoginId = 0
)
}

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -42,7 +43,7 @@ class GradeStatisticsLocalTest {
)
runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Matematyka") }
val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() }
assertEquals(1, stats.size)
assertEquals(stats[0].subject, "Matematyka")
}
@ -56,11 +57,12 @@ class GradeStatisticsLocalTest {
)
runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Wszystkie") }
assertEquals(3, stats.size)
assertEquals(stats[0].subject, "Wszystkie")
assertEquals(stats[1].subject, "Matematyka")
assertEquals(stats[2].subject, "Chemia")
val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() }
assertEquals(2, stats.size)
// assertEquals(3, stats.size)
// assertEquals(stats[0].subject, "Wszystkie") // now in main repo
assertEquals(stats[0].subject, "Matematyka")
assertEquals(stats[1].subject, "Chemia")
}
@Test
@ -72,7 +74,7 @@ class GradeStatisticsLocalTest {
)
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka") }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
with(stats[0]) {
assertEquals(subject, "Matematyka")
assertEquals(others, 5.0)
@ -84,7 +86,7 @@ class GradeStatisticsLocalTest {
fun saveAndRead_subjectEmpty() {
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka") }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
assertEquals(emptyList(), stats)
}
@ -92,7 +94,7 @@ class GradeStatisticsLocalTest {
fun saveAndRead_allEmpty() {
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Wszystkie") }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
assertEquals(emptyList(), stats)
}

View File

@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -41,7 +42,7 @@ class LuckyNumberLocalTest {
runBlocking { luckyNumberLocal.saveLuckyNumber(number) }
val student = Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", 1, false, now())
val luckyNumber = runBlocking { luckyNumberLocal.getLuckyNumber(student, LocalDate.of(2019, 1, 20)) }
val luckyNumber = runBlocking { luckyNumberLocal.getLuckyNumber(student, LocalDate.of(2019, 1, 20)).first() }
assertEquals(1, luckyNumber?.studentId)
assertEquals(LocalDate.of(2019, 1, 20), luckyNumber?.date)

View File

@ -4,6 +4,7 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.TestDispatchersProvider
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.repositories.getStudent
import kotlinx.coroutines.runBlocking
@ -27,7 +28,7 @@ class StudentLocalTest {
val context = ApplicationProvider.getApplicationContext<Context>()
testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.build()
studentLocal = StudentLocal(testDb.studentDao, context)
studentLocal = StudentLocal(testDb.studentDao, TestDispatchersProvider(), context)
}
@After

View File

@ -5,6 +5,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -48,7 +49,7 @@ class TimetableLocalTest {
semester = semester,
startDate = LocalDate.of(2018, 9, 10),
endDate = LocalDate.of(2018, 9, 14)
)
).first()
}
assertEquals(2, exams.size)

View File

@ -5,17 +5,16 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.getSemester
import io.github.wulkanowy.data.repositories.getStudent
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -29,45 +28,25 @@ import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
class TimetableRepositoryTest {
@MockK
private lateinit var mockSdk: Sdk
@MockK
private lateinit var studentMock: Student
private val student = getStudent()
@MockK
private lateinit var semesterMock: Semester
@MockK
@MockK(relaxed = true)
private lateinit var timetableNotificationSchedulerHelper: TimetableNotificationSchedulerHelper
@MockK
private lateinit var timetableRemote: TimetableRemote
private lateinit var timetableLocal: TimetableLocal
private lateinit var testDb: AppDatabase
private val student = getStudent()
private val semester = getSemester()
@Before
fun initApi() {
MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
timetableLocal = TimetableLocal(testDb.timetableDao)
timetableRemote = TimetableRemote(mockSdk)
every { timetableNotificationSchedulerHelper.scheduleNotifications(any(), any()) } returns mockk()
every { timetableNotificationSchedulerHelper.cancelScheduled(any(), any()) } returns mockk()
every { studentMock.studentId } returns 1
every { studentMock.studentName } returns "Jan Kowalski"
every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 2
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
}
@After
@ -86,21 +65,21 @@ class TimetableRepositoryTest {
))
}
coEvery { mockSdk.getTimetable(any(), any()) } returns listOf(
createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"),
createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia"),
createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F"),
createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F")
coEvery { timetableRemote.getTimetable(student, semester, any(), any()) } returns listOf(
createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"),
createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "", "Religia"),
createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "", "W-F"),
createTimetableLocal(of(2019, 3, 5, 10, 30), 4, "", "W-F")
)
val lessons = runBlocking {
TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable(
student = student,
semester = semesterMock,
semester = semester,
start = LocalDate.of(2019, 3, 5),
end = LocalDate.of(2019, 3, 5),
forceRefresh = true
)
).filter { it.status == Status.SUCCESS }.first().data.orEmpty()
}
assertEquals(4, lessons.size)
@ -129,31 +108,31 @@ class TimetableRepositoryTest {
)
runBlocking { timetableLocal.saveTimetable(list) }
coEvery { mockSdk.getTimetable(any(), any()) } returns listOf(
createTimetableRemote(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableRemote(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true),
createTimetableRemote(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false),
createTimetableRemote(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true),
coEvery { timetableRemote.getTimetable(student, semester, any(), any()) } returns listOf(
createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true),
createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false),
createTimetableLocal(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true),
createTimetableRemote(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false),
createTimetableRemote(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true),
createTimetableRemote(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false),
createTimetableRemote(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true),
createTimetableLocal(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false),
createTimetableLocal(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true),
createTimetableLocal(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false),
createTimetableLocal(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true),
createTimetableRemote(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true),
createTimetableRemote(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
createTimetableLocal(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false),
createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true),
createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false),
createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
)
val lessons = runBlocking {
TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable(
student = student,
semester = semesterMock,
semester = semester,
start = LocalDate.of(2019, 12, 23),
end = LocalDate.of(2019, 12, 25),
forceRefresh = true
)
).filter { it.status == Status.SUCCESS }.first().data.orEmpty()
}
assertEquals(12, lessons.size)

View File

@ -18,8 +18,7 @@
android:supportsRtl="false"
android:theme="@style/WulkanowyTheme"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"
tools:replace="android:supportsRtl,android:allowBackup">
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<activity
android:name=".ui.modules.splash.SplashActivity"
android:screenOrientation="portrait"

View File

@ -0,0 +1,23 @@
package io.github.wulkanowy.data
data class Resource<out T>(val status: Status, val data: T?, val error: Throwable?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(error: Throwable?, data: T? = null): Resource<T> {
return Resource(Status.ERROR, data, error)
}
fun <T> loading(data: T? = null): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
enum class Status {
LOADING,
SUCCESS,
ERROR
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Attendance
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface AttendanceDao : BaseDao<Attendance> {
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Attendance>
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Attendance>>
}

View File

@ -3,10 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import kotlinx.coroutines.flow.Flow
@Dao
interface AttendanceSummaryDao : BaseDao<AttendanceSummary> {
@Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId")
suspend fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): List<AttendanceSummary>
fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Flow<List<AttendanceSummary>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.CompletedLesson
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface CompletedLessonsDao : BaseDao<CompletedLesson> {
@Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<CompletedLesson>
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<CompletedLesson>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Exam
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface ExamDao : BaseDao<Exam> {
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Exam>
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Exam>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Grade
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface GradeDao : BaseDao<Grade> {
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
suspend fun loadAll(semesterId: Int, studentId: Int): List<Grade>
fun loadAll(semesterId: Int, studentId: Int): Flow<List<Grade>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,8 +11,8 @@ import javax.inject.Singleton
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
suspend fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): List<GradePointsStatistics>
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Flow<List<GradePointsStatistics>>
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
suspend fun loadAll(semesterId: Int, studentId: Int): List<GradePointsStatistics>
fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradePointsStatistics>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeStatistics
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,8 +11,8 @@ import javax.inject.Singleton
interface GradeStatisticsDao : BaseDao<GradeStatistics> {
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester")
suspend fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): List<GradeStatistics>
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Flow<List<GradeStatistics>>
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester")
suspend fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): List<GradeStatistics>
fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Flow<List<GradeStatistics>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeSummary
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface GradeSummaryDao : BaseDao<GradeSummary> {
@Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId")
suspend fun loadAll(semesterId: Int, studentId: Int): List<GradeSummary>
fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradeSummary>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Homework
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface HomeworkDao : BaseDao<Homework> {
@Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Homework>
fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Homework>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.LuckyNumber
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface LuckyNumberDao : BaseDao<LuckyNumber> {
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
suspend fun load(studentId: Int, date: LocalDate): LuckyNumber
fun load(studentId: Int, date: LocalDate): Flow<LuckyNumber>
}

View File

@ -5,17 +5,18 @@ import androidx.room.Query
import androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import kotlinx.coroutines.flow.Flow
@Dao
interface MessagesDao : BaseDao<Message> {
@Transaction
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
suspend fun loadMessageWithAttachment(studentId: Int, messageId: Int): MessageWithAttachment
fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow<MessageWithAttachment>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC")
suspend fun loadAll(studentId: Int, folder: Int): List<Message>
fun loadAll(studentId: Int, folder: Int): Flow<List<Message>>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
suspend fun loadDeleted(studentId: Int): List<Message>
fun loadDeleted(studentId: Int): Flow<List<Message>>
}

View File

@ -3,10 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.MobileDevice
import kotlinx.coroutines.flow.Flow
@Dao
interface MobileDeviceDao : BaseDao<MobileDevice> {
@Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC")
suspend fun loadAll(studentId: Int): List<MobileDevice>
fun loadAll(studentId: Int): Flow<List<MobileDevice>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Note
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface NoteDao : BaseDao<Note> {
@Query("SELECT * FROM Notes WHERE student_id = :studentId")
suspend fun loadAll(studentId: Int): List<Note>
fun loadAll(studentId: Int): Flow<List<Note>>
}

View File

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

View File

@ -3,10 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Subject
import kotlinx.coroutines.flow.Flow
@Dao
interface SubjectDao : BaseDao<Subject> {
@Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId")
suspend fun loadAll(diaryId: Int, studentId: Int): List<Subject>
fun loadAll(diaryId: Int, studentId: Int): Flow<List<Subject>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Teacher
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface TeacherDao : BaseDao<Teacher> {
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId")
suspend fun loadAll(studentId: Int, classId: Int): List<Teacher>
fun loadAll(studentId: Int, classId: Int): Flow<List<Teacher>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Timetable
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface TimetableDao : BaseDao<Timetable> {
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Timetable>
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
}

View File

@ -14,12 +14,10 @@ class AppCreatorRepository @Inject constructor(
private val dispatchers: DispatchersProvider
) {
suspend fun getAppCreators(): List<Contributor> {
return withContext(dispatchers.backgroundThread) {
Gson().fromJson(
assets.open("contributors.json").bufferedReader().use { it.readText() },
Array<Contributor>::class.java
).toList()
}
suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) {
Gson().fromJson(
assets.open("contributors.json").bufferedReader().use { it.readText() },
Array<Contributor>::class.java
).toList()
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -18,7 +19,7 @@ class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDa
attendanceDb.deleteAll(attendance)
}
suspend fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Attendance> {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Attendance>> {
return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
}
}

View File

@ -4,6 +4,7 @@ import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
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 org.threeten.bp.LocalDate
@ -16,19 +17,18 @@ class AttendanceRepository @Inject constructor(
private val remote: AttendanceRemote
) {
suspend fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean): List<Attendance> {
return local.getAttendance(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getAttendance(student, semester, start.monday, end.sunday)
val old = local.getAttendance(semester, start.monday, end.sunday)
fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getAttendance(semester, start.monday, end.sunday) },
fetch = { remote.getAttendance(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteAttendance(old uniqueSubtract new)
local.saveAttendance(new uniqueSubtract old)
},
filterResult = { it.filter { item -> item.date in start..end } }
)
local.deleteAttendance(old.uniqueSubtract(new))
local.saveAttendance(new.uniqueSubtract(old))
local.getAttendance(semester, start.monday, end.sunday)
}.filter { it.date in start..end }
}
suspend fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Boolean {
return remote.excuseAbsence(student, semester, attendanceList, reason)
suspend fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List<Attendance>, reason: String? = null) {
remote.excuseAbsence(student, semester, attendanceList, reason)
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -17,7 +18,7 @@ class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: Atten
attendanceDb.deleteAll(attendance)
}
suspend fun getAttendanceSummary(semester: Semester, subjectId: Int): List<AttendanceSummary> {
fun getAttendanceSummary(semester: Semester, subjectId: Int): Flow<List<AttendanceSummary>> {
return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId)
}
}

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -13,15 +13,13 @@ class AttendanceSummaryRepository @Inject constructor(
private val remote: AttendanceSummaryRemote
) {
suspend fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean = false): List<AttendanceSummary> {
return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh }.ifEmpty {
val new = remote.getAttendanceSummary(student, semester, subjectId)
val old = local.getAttendanceSummary(semester, subjectId)
local.deleteAttendanceSummary(old.uniqueSubtract(new))
local.saveAttendanceSummary(new.uniqueSubtract(old))
return local.getAttendanceSummary(semester, subjectId)
fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getAttendanceSummary(semester, subjectId) },
fetch = { remote.getAttendanceSummary(student, semester, subjectId) },
saveFetchResult = { old, new ->
local.deleteAttendanceSummary(old uniqueSubtract new)
local.saveAttendanceSummary(new uniqueSubtract old)
}
}
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -18,7 +19,7 @@ class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb:
completedLessonsDb.deleteAll(completedLessons)
}
suspend fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): List<CompletedLesson> {
fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Flow<List<CompletedLesson>> {
return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end)
}
}

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
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 org.threeten.bp.LocalDate
@ -16,15 +16,14 @@ class CompletedLessonsRepository @Inject constructor(
private val remote: CompletedLessonsRemote
) {
suspend fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<CompletedLesson> {
return local.getCompletedLessons(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getCompletedLessons(student, semester, start.monday, end.sunday)
val old = local.getCompletedLessons(semester, start.monday, end.sunday)
local.deleteCompleteLessons(old.uniqueSubtract(new))
local.saveCompletedLessons(new.uniqueSubtract(old))
local.getCompletedLessons(semester, start.monday, end.sunday)
}.filter { it.date in start..end }
}
fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getCompletedLessons(semester, start.monday, end.sunday) },
fetch = { remote.getCompletedLessons(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteCompleteLessons(old uniqueSubtract new)
local.saveCompletedLessons(new uniqueSubtract old)
},
filterResult = { it.filter { item -> item.date in start..end } }
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.data.db.dao.ExamDao
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -10,7 +11,7 @@ import javax.inject.Singleton
@Singleton
class ExamLocal @Inject constructor(private val examDb: ExamDao) {
suspend fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Exam> {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Exam>> {
return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
}

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
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 org.threeten.bp.LocalDate
@ -16,15 +16,14 @@ class ExamRepository @Inject constructor(
private val remote: ExamRemote
) {
suspend fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<Exam> {
return local.getExams(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getExams(student, semester, start.monday, end.sunday)
val old = local.getExams(semester, start.monday, end.sunday)
local.deleteExams(old.uniqueSubtract(new))
local.saveExams(new.uniqueSubtract(old))
local.getExams(semester, start.monday, end.sunday)
}.filter { it.date in start..end }
}
fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getExams(semester, start.monday, end.sunday) },
fetch = { remote.getExams(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteExams(old uniqueSubtract new)
local.saveExams(new uniqueSubtract old)
},
filterResult = { it.filter { item -> item.date in start..end } }
)
}

View File

@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -30,7 +31,7 @@ class GradeLocal @Inject constructor(
gradeSummaryDb.updateAll(gradesSummary)
}
suspend fun getGradesDetails(semester: Semester): List<Grade> {
fun getGradesDetails(semester: Semester): Flow<List<Grade>> {
return gradeDb.loadAll(semester.semesterId, semester.studentId)
}
@ -42,7 +43,7 @@ class GradeLocal @Inject constructor(
gradeSummaryDb.deleteAll(gradesSummary)
}
suspend fun getGradesSummary(semester: Semester): List<GradeSummary> {
fun getGradesSummary(semester: Semester): Flow<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
}
}

View File

@ -4,7 +4,11 @@ import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import org.threeten.bp.LocalDateTime
import javax.inject.Inject
import javax.inject.Singleton
@ -15,30 +19,30 @@ class GradeRepository @Inject constructor(
private val remote: GradeRemote
) {
suspend fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Pair<List<Grade>, List<GradeSummary>> {
val details = local.getGradesDetails(semester)
val summaries = local.getGradesSummary(semester)
if ((details.isNotEmpty() || summaries.isNotEmpty()) && !forceRefresh) {
return details to summaries
fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh },
query = { local.getGradesDetails(semester).combine(local.getGradesSummary(semester)) { details, summaries -> details to summaries } },
fetch = { remote.getGrades(student, semester) },
saveFetchResult = { old, new ->
refreshGradeDetails(student, old.first, new.first, notify)
refreshGradeSummaries(old.second, new.second, notify)
}
)
val (newDetails, newSummary) = remote.getGrades(student, semester)
val oldGrades = local.getGradesDetails(semester)
private suspend fun refreshGradeDetails(student: Student, oldGrades: List<Grade>, newDetails: List<Grade>, notify: Boolean) {
val notifyBreakDate = oldGrades.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate()
local.deleteGrades(oldGrades.uniqueSubtract(newDetails))
local.saveGrades(newDetails.uniqueSubtract(oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
isRead = false
if (notify) isNotified = false
}
})
local.deleteGrades(oldGrades uniqueSubtract newDetails)
local.saveGrades((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
isRead = false
if (notify) isNotified = false
}
})
}
val oldSummaries = local.getGradesSummary(semester)
local.deleteGradesSummary(oldSummaries.uniqueSubtract(newSummary))
local.saveGradesSummary(newSummary.uniqueSubtract(oldSummaries).onEach { summary ->
private suspend fun refreshGradeSummaries(oldSummaries: List<GradeSummary>, newSummary: List<GradeSummary>, notify: Boolean) {
local.deleteGradesSummary(oldSummaries uniqueSubtract newSummary)
local.saveGradesSummary((newSummary uniqueSubtract oldSummaries).onEach { summary ->
val oldSummary = oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject }
summary.isPredictedGradeNotified = when {
summary.predictedGrade.isEmpty() -> true
@ -62,24 +66,22 @@ class GradeRepository @Inject constructor(
else -> oldSummary.finalGradeLastChange
}
})
return local.getGradesDetails(semester) to local.getGradesSummary(semester)
}
suspend fun getUnreadGrades(semester: Semester): List<Grade> {
return local.getGradesDetails(semester).filter { grade -> !grade.isRead }
fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {
return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isRead } }
}
suspend fun getNotNotifiedGrades(semester: Semester): List<Grade> {
return local.getGradesDetails(semester).filter { grade -> !grade.isNotified }
fun getNotNotifiedGrades(semester: Semester): Flow<List<Grade>> {
return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isNotified } }
}
suspend fun getNotNotifiedPredictedGrades(semester: Semester): List<GradeSummary> {
return local.getGradesSummary(semester).filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified }
fun getNotNotifiedPredictedGrades(semester: Semester): Flow<List<GradeSummary>> {
return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } }
}
suspend fun getNotNotifiedFinalGrades(semester: Semester): List<GradeSummary> {
return local.getGradesSummary(semester).filter { gradeSummary -> !gradeSummary.isFinalGradeNotified }
fun getNotNotifiedFinalGrades(semester: Semester): Flow<List<GradeSummary>> {
return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } }
}
suspend fun updateGrade(grade: Grade) {

View File

@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -14,34 +15,14 @@ class GradeStatisticsLocal @Inject constructor(
private val gradePointsStatisticsDb: GradePointsStatisticsDao
) {
suspend fun getGradesStatistics(semester: Semester, isSemester: Boolean): List<GradeStatistics> {
fun getGradesStatistics(semester: Semester, isSemester: Boolean): Flow<List<GradeStatistics>> {
return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
}
suspend fun getGradesPointsStatistics(semester: Semester): List<GradePointsStatistics> {
fun getGradesPointsStatistics(semester: Semester): Flow<List<GradePointsStatistics>> {
return gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
}
suspend fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): List<GradeStatistics> {
return when (subjectName) {
"Wszystkie" -> {
val statistics = gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
statistics.groupBy { it.grade }.map {
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
it.value.fold(0) { acc, e -> acc + e.amount }, false)
} + statistics
}
else -> gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)
}
}
suspend fun getGradesPointsStatistics(semester: Semester, subjectName: String): List<GradePointsStatistics> {
return when (subjectName) {
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
}
}
suspend fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
gradeStatisticsDb.insertAll(gradesStatistics)
}

View File

@ -6,6 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -16,29 +17,40 @@ class GradeStatisticsRepository @Inject constructor(
private val remote: GradeStatisticsRemote
) {
suspend fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): List<GradeStatisticsItem> {
return local.getGradesStatistics(semester, isSemester, subjectName).mapToStatisticItems().filter { !forceRefresh }.ifEmpty {
val new = remote.getGradeStatistics(student, semester, isSemester)
val old = local.getGradesStatistics(semester, isSemester)
local.deleteGradesStatistics(old.uniqueSubtract(new))
local.saveGradesStatistics(new.uniqueSubtract(old))
local.getGradesStatistics(semester, isSemester, subjectName).mapToStatisticItems()
fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getGradesStatistics(semester, isSemester) },
fetch = { remote.getGradeStatistics(student, semester, isSemester) },
saveFetchResult = { old, new ->
local.deleteGradesStatistics(old uniqueSubtract new)
local.saveGradesStatistics(new uniqueSubtract old)
},
mapResult = { items ->
when (subjectName) {
"Wszystkie" -> items.groupBy { it.grade }.map {
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
it.value.fold(0) { acc, e -> acc + e.amount }, false)
} + items
else -> items.filter { it.subject == subjectName }
}.mapToStatisticItems()
}
}
)
suspend fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean): List<GradeStatisticsItem> {
return local.getGradesPointsStatistics(semester, subjectName).mapToStatisticsItem().filter { !forceRefresh }.ifEmpty {
val new = remote.getGradePointsStatistics(student, semester)
val old = local.getGradesPointsStatistics(semester)
local.deleteGradesPointsStatistics(old.uniqueSubtract(new))
local.saveGradesPointsStatistics(new.uniqueSubtract(old))
local.getGradesPointsStatistics(semester, subjectName).mapToStatisticsItem()
fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getGradesPointsStatistics(semester) },
fetch = { remote.getGradePointsStatistics(student, semester) },
saveFetchResult = { old, new ->
local.deleteGradesPointsStatistics(old uniqueSubtract new)
local.saveGradesPointsStatistics(new uniqueSubtract old)
},
mapResult = { items ->
when (subjectName) {
"Wszystkie" -> items
else -> items.filter { it.subject == subjectName }
}.mapToStatisticsItem()
}
}
)
private fun List<GradeStatistics>.mapToStatisticItems() = groupBy { it.subject }.map {
GradeStatisticsItem(

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.homework
import io.github.wulkanowy.data.db.dao.HomeworkDao
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -22,7 +23,7 @@ class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
homeworkDb.updateAll(homework)
}
suspend fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Homework> {
fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Homework>> {
return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate)
}
}

View File

@ -4,6 +4,7 @@ import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
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 org.threeten.bp.LocalDate
@ -16,18 +17,15 @@ class HomeworkRepository @Inject constructor(
private val remote: HomeworkRemote
) {
suspend fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<Homework> {
return local.getHomework(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getHomework(student, semester, start.monday, end.sunday)
val old = local.getHomework(semester, start.monday, end.sunday)
local.deleteHomework(old.uniqueSubtract(new))
local.saveHomework(new.uniqueSubtract(old))
local.getHomework(semester, start.monday, end.sunday)
fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getHomework(semester, start.monday, end.sunday) },
fetch = { remote.getHomework(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteHomework(old uniqueSubtract new)
local.saveHomework(new uniqueSubtract old)
}
}
)
suspend fun toggleDone(homework: Homework) {
local.updateHomework(listOf(homework.apply {

View File

@ -12,16 +12,12 @@ class LoggerRepository @Inject constructor(
private val dispatchers: DispatchersProvider
) {
suspend fun getLastLogLines(): List<String> {
return getLastModified().readText().split("\n")
}
suspend fun getLastLogLines() = getLastModified().readText().split("\n")
suspend fun getLogFiles(): List<File> {
return withContext(dispatchers.backgroundThread) {
File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter {
it.name.endsWith(".log")
}!!
}
suspend fun getLogFiles() = withContext(dispatchers.backgroundThread) {
File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter {
it.name.endsWith(".log")
}!!
}
private suspend fun getLastModified(): File {

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -22,7 +23,7 @@ class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumbe
luckyNumberDb.deleteAll(listOfNotNull(luckyNumber))
}
suspend fun getLuckyNumber(student: Student, date: LocalDate): LuckyNumber? {
fun getLuckyNumber(student: Student, date: LocalDate): Flow<LuckyNumber?> {
return luckyNumberDb.load(student.studentId, date)
}
}

View File

@ -2,6 +2,8 @@ package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate.now
import javax.inject.Inject
import javax.inject.Singleton
@ -12,31 +14,25 @@ class LuckyNumberRepository @Inject constructor(
private val remote: LuckyNumberRemote
) {
suspend fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): LuckyNumber? {
return local.getLuckyNumber(student, now())?.takeIf { !forceRefresh } ?: run {
val new = remote.getLuckyNumber(student)
val old = local.getLuckyNumber(student, now())
fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { it == null || forceRefresh },
query = { local.getLuckyNumber(student, now()) },
fetch = { remote.getLuckyNumber(student) },
saveFetchResult = { old, new ->
if (new != old) {
old?.let { local.deleteLuckyNumber(it) }
local.saveLuckyNumber(new?.apply {
if (notify) isNotified = false
})
}
local.saveLuckyNumber(new?.apply {
if (notify) isNotified = false
})
local.getLuckyNumber(student, now())
}
}
)
suspend fun getNotNotifiedLuckyNumber(student: Student): LuckyNumber? {
fun getNotNotifiedLuckyNumber(student: Student): Flow<LuckyNumber?> {
return local.getLuckyNumber(student, now())
}
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber) {
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) {
local.updateLuckyNumber(luckyNumber)
}
}

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -28,7 +29,7 @@ class MessageLocal @Inject constructor(
messagesDb.deleteAll(messages)
}
suspend fun getMessageWithAttachment(student: Student, message: Message): MessageWithAttachment {
fun getMessageWithAttachment(student: Student, message: Message): Flow<MessageWithAttachment> {
return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId)
}
@ -36,7 +37,7 @@ class MessageLocal @Inject constructor(
messageAttachmentDao.insertAttachments(attachments)
}
suspend fun getMessages(student: Student, folder: MessageFolder): List<Message> {
fun getMessages(student: Student, folder: MessageFolder): Flow<List<Message>> {
return when (folder) {
TRASHED -> messagesDb.loadDeleted(student.id.toInt())
else -> messagesDb.loadAll(student.id.toInt(), folder.id)

View File

@ -1,13 +1,15 @@
package io.github.wulkanowy.data.repositories.message
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
import io.github.wulkanowy.sdk.pojo.SentMessage
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@ -18,46 +20,37 @@ class MessageRepository @Inject constructor(
private val remote: MessageRemote
) {
suspend fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): List<Message> {
return local.getMessages(student, folder).filter { !forceRefresh }.ifEmpty {
val new = remote.getMessages(student, semester, folder)
val old = local.getMessages(student, folder)
local.deleteMessages(old.uniqueSubtract(new))
local.saveMessages(new.uniqueSubtract(old).onEach {
fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getMessages(student, folder) },
fetch = { remote.getMessages(student, semester, folder) },
saveFetchResult = { old, new ->
local.deleteMessages(old uniqueSubtract new)
local.saveMessages((new uniqueSubtract old).onEach {
it.isNotified = !notify
})
local.getMessages(student, folder)
}
}
)
suspend fun getMessage(student: Student, message: Message, markAsRead: Boolean = false): MessageWithAttachment {
return local.getMessageWithAttachment(student, message).let {
if (it.message.content.isNotEmpty().also { status ->
Timber.d("Message content in db empty: ${!status}")
} && !it.message.unread) {
return@let it
}
val dbMessage = local.getMessageWithAttachment(student, message)
val (downloadedMessage, attachments) = remote.getMessagesContentDetails(student, dbMessage.message, markAsRead)
local.updateMessages(listOf(dbMessage.message.copy(unread = !markAsRead).apply {
id = dbMessage.message.id
fun getMessage(student: Student, message: Message, markAsRead: Boolean = false) = networkBoundResource(
shouldFetch = {
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
it.message.unread || it.message.content.isEmpty()
},
query = { local.getMessageWithAttachment(student, message) },
fetch = { remote.getMessagesContentDetails(student, it.message, markAsRead) },
saveFetchResult = { old, (downloadedMessage, attachments) ->
local.updateMessages(listOf(old.message.copy(unread = !markAsRead).apply {
id = old.message.id
content = content.ifBlank { downloadedMessage }
}))
local.saveMessageAttachments(attachments)
Timber.d("Message ${message.messageId} with blank content: ${dbMessage.message.content.isBlank()}, marked as read")
local.getMessageWithAttachment(student, message)
Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read")
}
}
)
suspend fun getNotNotifiedMessages(student: Student): List<Message> {
return local.getMessages(student, RECEIVED)
.filter { message -> !message.isNotified && message.unread }
fun getNotNotifiedMessages(student: Student): Flow<List<Message>> {
return local.getMessages(student, RECEIVED).map { it.filter { message -> !message.isNotified && message.unread } }
}
suspend fun updateMessages(messages: List<Message>) {
@ -68,15 +61,12 @@ class MessageRepository @Inject constructor(
return remote.sendMessage(student, subject, content, recipients)
}
suspend fun deleteMessage(student: Student, message: Message): Boolean {
val delete = remote.deleteMessage(student, message)
suspend fun deleteMessage(student: Student, message: Message) {
val isDeleted = remote.deleteMessage(student, message)
if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply {
if (!message.removed) local.updateMessages(listOf(message.copy(removed = isDeleted).apply {
id = message.id
content = message.content
}))
else local.deleteMessages(listOf(message))
return delete // TODO: wtf
})) else local.deleteMessages(listOf(message))
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.mobiledevice
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -17,7 +18,7 @@ class MobileDeviceLocal @Inject constructor(private val mobileDb: MobileDeviceDa
mobileDb.deleteAll(devices)
}
suspend fun getDevices(semester: Semester): List<MobileDevice> {
fun getDevices(semester: Semester): Flow<List<MobileDevice>> {
return mobileDb.loadAll(semester.studentId)
}
}

View File

@ -4,6 +4,7 @@ import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -14,20 +15,19 @@ class MobileDeviceRepository @Inject constructor(
private val remote: MobileDeviceRemote
) {
suspend fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean = false): List<MobileDevice> {
return local.getDevices(semester).filter { !forceRefresh }.ifEmpty {
val new = remote.getDevices(student, semester)
val old = local.getDevices(semester)
fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getDevices(semester) },
fetch = { remote.getDevices(student, semester) },
saveFetchResult = { old, new ->
local.deleteDevices(old uniqueSubtract new)
local.saveDevices(new uniqueSubtract old)
local.getDevices(semester)
}
}
)
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice): Boolean {
return remote.unregisterDevice(student, semester, device)
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
remote.unregisterDevice(student, semester, device)
local.deleteDevices(listOf(device))
}
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.note
import io.github.wulkanowy.data.db.dao.NoteDao
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -21,7 +22,7 @@ class NoteLocal @Inject constructor(private val noteDb: NoteDao) {
noteDb.deleteAll(notes)
}
suspend fun getNotes(student: Student): List<Note> {
fun getNotes(student: Student): Flow<List<Note>> {
return noteDb.loadAll(student.studentId)
}
}

View File

@ -3,7 +3,10 @@ package io.github.wulkanowy.data.repositories.note
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton
@ -13,29 +16,27 @@ class NoteRepository @Inject constructor(
private val remote: NoteRemote
) {
suspend fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): List<Note> {
return local.getNotes(student).filter { !forceRefresh }.ifEmpty {
val new = remote.getNotes(student, semester)
val old = local.getNotes(student)
local.deleteNotes(old.uniqueSubtract(new))
local.saveNotes(new.uniqueSubtract(old).onEach {
fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getNotes(student) },
fetch = { remote.getNotes(student, semester) },
saveFetchResult = { old, new ->
local.deleteNotes(old uniqueSubtract new)
local.saveNotes((new uniqueSubtract old).onEach {
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
isRead = false
if (notify) isNotified = false
}
})
local.getNotes(student)
}
}
)
suspend fun getNotNotifiedNotes(student: Student): List<Note> {
return local.getNotes(student).filter { note -> !note.isNotified }
fun getNotNotifiedNotes(student: Student): Flow<List<Note>> {
return local.getNotes(student).map { it.filter { note -> !note.isNotified } }
}
suspend fun updateNote(note: Note) {
return local.updateNotes(listOf(note))
local.updateNotes(listOf(note))
}
suspend fun updateNotes(notes: List<Note>) {

View File

@ -14,13 +14,17 @@ class RecipientRepository @Inject constructor(
private val remote: RecipientRemote
) {
suspend fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): List<Recipient> {
return local.getRecipients(student, role, unit).filter { !forceRefresh }.ifEmpty {
val new = remote.getRecipients(student, role, unit)
val old = local.getRecipients(student, role, unit)
suspend fun refreshRecipients(student: Student, role: Int, unit: ReportingUnit) {
val new = remote.getRecipients(student, role, unit)
val old = local.getRecipients(student, role, unit)
local.deleteRecipients(old.uniqueSubtract(new))
local.saveRecipients(new.uniqueSubtract(old))
local.deleteRecipients(old uniqueSubtract new)
local.saveRecipients(new uniqueSubtract old)
}
suspend fun getRecipients(student: Student, role: Int, unit: ReportingUnit): List<Recipient> {
return local.getRecipients(student, role, unit).ifEmpty {
refreshRecipients(student, role, unit)
local.getRecipients(student, role, unit)
}

View File

@ -12,23 +12,27 @@ class ReportingUnitRepository @Inject constructor(
private val remote: ReportingUnitRemote
) {
suspend fun getReportingUnits(student: Student, forceRefresh: Boolean = false): List<ReportingUnit> {
return local.getReportingUnits(student).filter { !forceRefresh }.ifEmpty {
val new = remote.getReportingUnits(student)
val old = local.getReportingUnits(student)
suspend fun refreshReportingUnits(student: Student) {
val new = remote.getReportingUnits(student)
val old = local.getReportingUnits(student)
local.deleteReportingUnits(old.uniqueSubtract(new))
local.saveReportingUnits(new.uniqueSubtract(old))
local.deleteReportingUnits(old.uniqueSubtract(new))
local.saveReportingUnits(new.uniqueSubtract(old))
}
suspend fun getReportingUnits(student: Student): List<ReportingUnit> {
return local.getReportingUnits(student).ifEmpty {
refreshReportingUnits(student)
local.getReportingUnits(student)
}
}
suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit {
suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? {
return local.getReportingUnit(student, unitId) ?: run {
getReportingUnits(student, true)
refreshReportingUnits(student)
return local.getReportingUnit(student, unitId)!!
return local.getReportingUnit(student, unitId)
}
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.data.db.dao.SchoolDao
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) {
@ -15,7 +16,7 @@ class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) {
schoolDb.deleteAll(listOf(school))
}
suspend fun getSchool(semester: Semester): School? {
fun getSchool(semester: Semester): Flow<School?> {
return schoolDb.load(semester.studentId, semester.classId)
}
}

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import javax.inject.Inject
import javax.inject.Singleton
@ -12,18 +12,16 @@ class SchoolRepository @Inject constructor(
private val remote: SchoolRemote
) {
suspend fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean = false): School {
return local.getSchool(semester).takeIf { it != null && !forceRefresh } ?: run {
val new = remote.getSchoolInfo(student, semester)
val old = local.getSchool(semester)
fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it == null || forceRefresh },
query = { local.getSchool(semester) },
fetch = { remote.getSchoolInfo(student, semester) },
saveFetchResult = { old, new ->
if (new != old && old != null) {
local.deleteSchool(old)
local.saveSchool(new)
}
local.saveSchool(new)
local.getSchool(semester)!!
}
}
)
}

View File

@ -1,22 +1,24 @@
package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.getCurrentOrLast
import io.github.wulkanowy.utils.isCurrent
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SemesterRepository @Inject constructor(
private val remote: SemesterRemote,
private val local: SemesterLocal
private val local: SemesterLocal,
private val dispatchers: DispatchersProvider
) {
suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): List<Semester> {
return local.getSemesters(student).let { semesters ->
suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) {
local.getSemesters(student).let { semesters ->
semesters.filter {
!forceRefresh && when {
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> semesters.firstOrNull { it.isCurrent }?.diaryId != 0
@ -36,7 +38,7 @@ class SemesterRepository @Inject constructor(
}
}
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Semester {
return getSemesters(student, forceRefresh).getCurrentOrLast()
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) {
getSemesters(student, forceRefresh).getCurrentOrLast()
}
}

View File

@ -4,52 +4,55 @@ import android.content.Context
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.security.decrypt
import io.github.wulkanowy.utils.security.encrypt
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class StudentLocal @Inject constructor(
private val studentDb: StudentDao,
private val dispatchers: DispatchersProvider,
private val context: Context
) {
suspend fun saveStudents(students: List<Student>): List<Long> {
return studentDb.insertAll(students.map {
suspend fun saveStudents(students: List<Student>) = withContext(dispatchers.backgroundThread) {
studentDb.insertAll(students.map {
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context))
else it
})
}
suspend fun getStudents(decryptPass: Boolean): List<Student> {
return studentDb.loadAll().map {
suspend fun getStudents(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) {
studentDb.loadAll().map {
it.apply {
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
}
}
}
suspend fun getStudentById(id: Int): Student? {
return studentDb.loadById(id)?.apply {
suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) {
studentDb.loadById(id)?.apply {
if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
}
}
suspend fun getCurrentStudent(decryptPass: Boolean): Student? {
return studentDb.loadCurrent()?.apply {
suspend fun getCurrentStudent(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) {
studentDb.loadCurrent()?.apply {
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
}
}
suspend fun setCurrentStudent(student: Student) {
return studentDb.run {
suspend fun setCurrentStudent(student: Student) = withContext(dispatchers.backgroundThread) {
studentDb.run {
resetCurrent()
updateCurrent(student.id)
}
}
suspend fun logoutStudent(student: Student) {
return studentDb.delete(student)
suspend fun logoutStudent(student: Student) = withContext(dispatchers.backgroundThread) {
studentDb.delete(student)
}
}

View File

@ -3,13 +3,14 @@ package io.github.wulkanowy.data.repositories.subject
import io.github.wulkanowy.data.db.dao.SubjectDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Subject
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) {
suspend fun getSubjects(semester: Semester): List<Subject> {
fun getSubjects(semester: Semester): Flow<List<Subject>> {
return subjectDao.loadAll(semester.diaryId, semester.studentId)
}

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.subject
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -13,15 +13,13 @@ class SubjectRepository @Inject constructor(
private val remote: SubjectRemote
) {
suspend fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false): List<Subject> {
return local.getSubjects(semester).filter { !forceRefresh }.ifEmpty {
val new = remote.getSubjects(student, semester)
val old = local.getSubjects(semester)
local.deleteSubjects(old.uniqueSubtract(new))
local.saveSubjects(new.uniqueSubtract(old))
local.getSubjects(semester)
fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getSubjects(semester) },
fetch = { remote.getSubjects(student, semester) },
saveFetchResult = { old, new ->
local.deleteSubjects(old uniqueSubtract new)
local.saveSubjects(new uniqueSubtract old)
}
}
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.teacher
import io.github.wulkanowy.data.db.dao.TeacherDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Teacher
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) {
@ -15,7 +16,7 @@ class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) {
teacherDb.deleteAll(teachers)
}
suspend fun getTeachers(semester: Semester): List<Teacher> {
fun getTeachers(semester: Semester): Flow<List<Teacher>> {
return teacherDb.loadAll(semester.studentId, semester.classId)
}
}

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.teacher
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -13,15 +13,13 @@ class TeacherRepository @Inject constructor(
private val remote: TeacherRemote
) {
suspend fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean = false): List<Teacher> {
return local.getTeachers(semester).filter { !forceRefresh }.ifEmpty {
val new = remote.getTeachers(student, semester)
val old = local.getTeachers(semester)
local.deleteTeachers(old.uniqueSubtract(new))
local.saveTeachers(new.uniqueSubtract(old))
local.getTeachers(semester)
fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getTeachers(semester) },
fetch = { remote.getTeachers(student, semester) },
saveFetchResult = { old, new ->
local.deleteTeachers(old uniqueSubtract new)
local.saveTeachers(new uniqueSubtract old)
}
}
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Timetable
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -18,7 +19,7 @@ class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao)
timetableDb.deleteAll(timetables)
}
suspend fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Timetable> {
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Timetable>> {
return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
}
}

View File

@ -2,11 +2,12 @@ package io.github.wulkanowy.data.repositories.timetable
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.services.alarm.TimetableNotificationSchedulerHelper
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.map
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -18,11 +19,11 @@ class TimetableRepository @Inject constructor(
private val schedulerHelper: TimetableNotificationSchedulerHelper
) {
suspend fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<Timetable> {
return local.getTimetable(semester, start.monday, start.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getTimetable(student, semester, start.monday, start.sunday)
val old = local.getTimetable(semester, start.monday, start.sunday)
fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getTimetable(semester, start.monday, end.sunday).map { schedulerHelper.scheduleNotifications(it, student); it } },
fetch = { remote.getTimetable(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteTimetable(old.uniqueSubtract(new).also { schedulerHelper.cancelScheduled(it) })
local.saveTimetable(new.uniqueSubtract(old).also { schedulerHelper.scheduleNotifications(it, student) }.map { item ->
item.also { new ->
@ -34,8 +35,7 @@ class TimetableRepository @Inject constructor(
}
}
})
local.getTimetable(semester, start.monday, start.sunday)
}.filter { it.date in start..end }.also { schedulerHelper.scheduleNotifications(it, student) }
}
},
filterResult = { it.filter { item -> item.date in start..end } }
)
}

View File

@ -12,7 +12,7 @@ class AttendanceSummaryWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true) }
return rxCompletable { attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true).waitForResult() }
}
}

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now
@ -13,6 +13,6 @@ import javax.inject.Inject
class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true) }
return rxCompletable { attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now
@ -15,7 +15,6 @@ class CompletedLessonWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().sunday, true) }
return rxCompletable { completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -13,6 +13,6 @@ import javax.inject.Inject
class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { examRepository.getExams(student, semester, now().monday, now().sunday, true) }
return rxCompletable { examRepository.getExams(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -7,10 +7,17 @@ import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable
import javax.inject.Inject
class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work {
class GradeStatisticsWork @Inject constructor(
private val gradeStatisticsRepository: GradeStatisticsRepository
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { gradeStatisticsRepository.getGradesStatistics(student, semester, "Wszystkie", false, forceRefresh = true) }
return rxCompletable {
with(gradeStatisticsRepository) {
getGradesStatistics(student, semester, "Wszystkie", isSemester = true, forceRefresh = true).waitForResult()
getGradesStatistics(student, semester, "Wszystkie", isSemester = false, forceRefresh = true).waitForResult()
getGradesPointsStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
}
}
}
}

View File

@ -19,6 +19,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject
@ -32,14 +33,14 @@ class GradeWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable) }
.concatWith(Completable.concatArray(rxSingle { gradeRepository.getNotNotifiedGrades(semester) }.flatMapCompletable {
return rxCompletable { gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.concatWith(Completable.concatArray(rxSingle { gradeRepository.getNotNotifiedGrades(semester).first() }.flatMapCompletable {
if (it.isNotEmpty()) notifyDetails(it)
rxCompletable { gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true }) }
}, rxSingle { gradeRepository.getNotNotifiedPredictedGrades(semester) }.flatMapCompletable {
}, rxSingle { gradeRepository.getNotNotifiedPredictedGrades(semester).first() }.flatMapCompletable {
if (it.isNotEmpty()) notifyPredicted(it)
rxCompletable { gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isPredictedGradeNotified = true }) }
}, rxSingle { gradeRepository.getNotNotifiedFinalGrades(semester) }.flatMapCompletable {
}, rxSingle { gradeRepository.getNotNotifiedFinalGrades(semester).first() }.flatMapCompletable {
if (it.isNotEmpty()) notifyFinal(it)
rxCompletable { gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isFinalGradeNotified = true }) }
}))

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now
@ -13,6 +13,6 @@ import javax.inject.Inject
class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { homeworkRepository.getHomework(student, semester, now().monday, now().sunday, true) }
return rxCompletable { homeworkRepository.getHomework(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxMaybe
import javax.inject.Inject
@ -31,8 +32,8 @@ class LuckyNumberWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxMaybe { luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable) }
.flatMap { rxMaybe { luckyNumberRepository.getNotNotifiedLuckyNumber(student) } }
return rxMaybe { luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxMaybe { luckyNumberRepository.getNotNotifiedLuckyNumber(student).first() } }
.flatMapCompletable {
notify(it)
rxCompletable { luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true }) }

View File

@ -19,6 +19,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject
@ -32,8 +33,8 @@ class MessageWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxSingle { messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable) }
.flatMap { rxSingle { messageRepository.getNotNotifiedMessages(student) } }
return rxSingle { messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxSingle { messageRepository.getNotNotifiedMessages(student).first() } }
.flatMapCompletable {
if (it.isNotEmpty()) notify(it)
rxCompletable { messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) }

View File

@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject
@ -31,8 +32,8 @@ class NoteWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxSingle { noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable) }
.flatMap { rxSingle { noteRepository.getNotNotifiedNotes(student) } }
return rxSingle { noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxSingle { noteRepository.getNotNotifiedNotes(student).first() } }
.flatMapCompletable {
if (it.isNotEmpty()) notify(it)
rxCompletable { noteRepository.updateNotes(it.onEach { note -> note.isNotified = true }) }

View File

@ -15,10 +15,11 @@ class RecipientWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxSingle { reportingUnitRepository.getReportingUnits(student, true) }
return rxSingle { reportingUnitRepository.refreshReportingUnits(student) }
.flatMap { rxSingle { reportingUnitRepository.getReportingUnits(student) } }
.flatMapCompletable { units ->
Completable.mergeDelayError(units.map {
rxCompletable { recipientRepository.getRecipients(student, 2, it, true) }
rxCompletable { recipientRepository.refreshRecipients(student, 2, it) }
})
}
}

View File

@ -10,6 +10,6 @@ import javax.inject.Inject
class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { teacherRepository.getTeachers(student, semester, true) }
return rxCompletable { teacherRepository.getTeachers(student, semester, true).waitForResult() }
}
}

View File

@ -13,6 +13,6 @@ import javax.inject.Inject
class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { timetableRepository.getTimetable(student, semester, now().monday, now().sunday, true) }
return rxCompletable { timetableRepository.getTimetable(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -1,11 +1,19 @@
package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Completable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.takeWhile
interface Work {
fun create(student: Student, semester: Semester): Completable
}
suspend fun <T> Flow<Resource<T>>.waitForResult() = takeWhile {
it.status == Status.LOADING
}.collect()
}

View File

@ -1,24 +1,39 @@
package io.github.wulkanowy.ui.base
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import io.github.wulkanowy.utils.flowWithResource
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
open class BasePresenter<T : BaseView>(
protected val errorHandler: ErrorHandler,
protected val studentRepository: StudentRepository,
protected val schedulers: SchedulersProvider
) {
) : CoroutineScope {
private var job: Job = Job()
private val jobs = mutableMapOf<String, Job>()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
@Deprecated("Use flow instead :)")
val disposable = CompositeDisposable()
var view: T? = null
open fun onAttachView(view: T) {
job = Job()
this.view = view
errorHandler.apply {
showErrorMessage = view::showError
@ -28,30 +43,48 @@ open class BasePresenter<T : BaseView>(
}
fun onExpiredLoginSelected() {
Timber.i("Attempt to switch the student after the session expires")
disposable.add(rxSingle { studentRepository.getCurrentStudent(false) }
.flatMapCompletable { rxCompletable { studentRepository.logoutStudent(it) } }
.andThen(rxSingle { studentRepository.getSavedStudents(false) })
.flatMapCompletable {
if (it.isNotEmpty()) {
Timber.i("Switching current student")
rxCompletable { studentRepository.switchStudent(it[0]) }
} else Completable.complete()
flowWithResource {
val student = studentRepository.getCurrentStudent(false)
studentRepository.logoutStudent(student)
val students = studentRepository.getSavedStudents(false)
if (students.isNotEmpty()) {
Timber.i("Switching current student")
studentRepository.switchStudent(students[0])
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Switch student result: Open login view")
view?.openClearLoginView()
}, {
Timber.i("Switch student result: An exception occurred")
errorHandler.dispatch(it)
}))
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to switch the student after the session expires")
Status.SUCCESS -> {
Timber.i("Switch student result: Open login view")
view?.openClearLoginView()
}
Status.ERROR -> {
Timber.i("Switch student result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch("expired")
}
fun <T> Flow<T>.launch(individualJobTag: String = "load"): Job {
jobs[individualJobTag]?.cancel()
val job = launchIn(this@BasePresenter)
jobs[individualJobTag] = job
Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job")
return job
}
fun cancelJobs(vararg names: String) {
names.forEach {
jobs[it]?.cancel()
}
}
open fun onDetachView() {
view = null
disposable.clear()
job.cancel()
errorHandler.clear()
}
}

View File

@ -24,10 +24,10 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
}
protected open fun proceed(error: Throwable) {
showErrorMessage(resources.getString(error), error)
when (error) {
is ScramblerException, is BadCredentialsException -> onSessionExpired()
is NoCurrentStudentException -> onNoCurrentStudent()
else -> showErrorMessage(resources.getString(error), error)
}
}

View File

@ -1,12 +1,14 @@
package io.github.wulkanowy.ui.modules.about.contributor
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.pojos.Contributor
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
class ContributorPresenter @Inject constructor(
@ -31,10 +33,15 @@ class ContributorPresenter @Inject constructor(
}
private fun loadData() {
disposable.add(rxSingle { appCreatorRepository.getAppCreators() }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.showProgress(false) }
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) }))
flowWithResource { appCreatorRepository.getAppCreators() }.onEach {
when (it.status) {
Status.LOADING -> view?.showProgress(true)
Status.SUCCESS -> view?.run {
showProgress(false)
updateData(it.data!!)
}
Status.ERROR -> errorHandler.dispatch(it.error!!)
}
}
}
}

View File

@ -24,7 +24,7 @@ class LicenseAdapter @Inject constructor() : RecyclerView.Adapter<LicenseAdapter
with(holder.binding) {
licenseItemName.text = item.libraryName
licenseItemSummary.text = item.license?.licenseName?.takeIf { it.isNotBlank() } ?: item.libraryVersion
licenseItemSummary.text = item.licenses?.firstOrNull()?.licenseName?.takeIf { it.isNotBlank() } ?: item.libraryVersion
root.setOnClickListener { onClickListener(item) }
}

View File

@ -1,15 +1,22 @@
package io.github.wulkanowy.ui.modules.about.license
import com.mikepenz.aboutlibraries.entity.Library
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Single
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
class LicensePresenter @Inject constructor(
schedulers: SchedulersProvider,
private val dispatchers: DispatchersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter<LicenseView>(errorHandler, studentRepository, schedulers) {
@ -21,14 +28,22 @@ class LicensePresenter @Inject constructor(
}
fun onItemSelected(library: Library) {
view?.run { library.license?.licenseDescription?.let { openLicense(it) } }
view?.run { library.licenses?.firstOrNull()?.licenseDescription?.let { openLicense(it) } }
}
private fun loadData() {
disposable.add(Single.fromCallable { view?.appLibraries.orEmpty() }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnEvent { _, _ -> view?.showProgress(false) }
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) }))
flowWithResource {
withContext(dispatchers.backgroundThread) {
view?.appLibraries.orEmpty()
}
}.onEach {
when (it.status) {
Status.LOADING -> Timber.d("License data load started")
Status.SUCCESS -> view?.updateData(it.data!!)
Status.ERROR -> errorHandler.dispatch(it.error!!)
}
}.afterLoading {
view?.showProgress(false)
}.launch()
}
}

View File

@ -1,11 +1,13 @@
package io.github.wulkanowy.ui.modules.about.logviewer
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.logger.LoggerRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -23,16 +25,19 @@ class LogViewerPresenter @Inject constructor(
}
fun onShareLogsSelected(): Boolean {
disposable.add(rxSingle { loggerRepository.getLogFiles() }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({ files ->
Timber.i("Loading logs files result: ${files.joinToString { it.name }}")
view?.shareLogs(files)
}, {
Timber.i("Loading logs files result: An exception occurred")
errorHandler.dispatch(it)
}))
flowWithResource { loggerRepository.getLogFiles() }.onEach {
when (it.status) {
Status.LOADING -> Timber.d("Loading logs files started")
Status.SUCCESS -> {
Timber.i("Loading logs files result: ${it.data!!.joinToString { file -> file.name }}")
view?.shareLogs(it.data)
}
Status.ERROR -> {
Timber.i("Loading logs files result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch("share")
return true
}
@ -41,15 +46,18 @@ class LogViewerPresenter @Inject constructor(
}
private fun loadLogFile() {
disposable.add(rxSingle { loggerRepository.getLastLogLines() }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Loading last log file result: load ${it.size} lines")
view?.setLines(it)
}, {
Timber.i("Loading last log file result: An exception occurred")
errorHandler.dispatch(it)
}))
flowWithResource { loggerRepository.getLastLogLines() }.onEach {
when (it.status) {
Status.LOADING -> Timber.d("Loading last log file started")
Status.SUCCESS -> {
Timber.i("Loading last log file result: load ${it.data!!.size} lines")
view?.setLines(it.data)
}
Status.ERROR -> {
Timber.i("Loading last log file result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch("file")
}
}

View File

@ -1,14 +1,15 @@
package io.github.wulkanowy.ui.modules.account
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Single
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -37,20 +38,20 @@ class AccountPresenter @Inject constructor(
}
fun onLogoutConfirm() {
Timber.i("Attempt to logout current user ")
disposable.add(rxSingle { studentRepository.getCurrentStudent(false) }
.flatMapCompletable { rxCompletable { studentRepository.logoutStudent(it) } }
.andThen(rxSingle { studentRepository.getSavedStudents(false) })
.flatMap {
if (it.isNotEmpty()) rxCompletable { studentRepository.switchStudent(it[0]) }.toSingle { it }
else Single.just(it)
flowWithResource {
val student = studentRepository.getCurrentStudent(false)
studentRepository.logoutStudent(student)
val students = studentRepository.getSavedStudents(false)
if (students.isNotEmpty()) {
studentRepository.switchStudent(students[0])
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.dismissView() }
.subscribe({
view?.apply {
if (it.isEmpty()) {
students
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to logout current user ")
Status.SUCCESS -> view?.run {
if (it.data!!.isEmpty()) {
Timber.i("Logout result: Open login view")
syncManager.stopSyncWorker()
openClearLoginView()
@ -59,30 +60,35 @@ class AccountPresenter @Inject constructor(
recreateMainView()
}
}
}, {
Timber.i("Logout result: An exception occurred")
errorHandler.dispatch(it)
}))
Status.ERROR -> {
Timber.i("Logout result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.dismissView()
}.launch("logout")
}
fun onItemSelected(student: Student) {
Timber.i("Select student item ${student.id}")
if (student.isCurrent) {
view?.dismissView()
} else {
Timber.i("Attempt to change a student")
disposable.add(rxSingle { studentRepository.switchStudent(student) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.dismissView() }
.subscribe({
} else flowWithResource { studentRepository.switchStudent(student) }.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to change a student")
Status.SUCCESS -> {
Timber.i("Change a student result: Success")
view?.recreateMainView()
}, {
}
Status.ERROR -> {
Timber.i("Change a student result: An exception occurred")
errorHandler.dispatch(it)
}))
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.dismissView()
}.launch("switch")
}
private fun createAccountItems(items: List<Student>): List<AccountItem<*>> {
@ -94,17 +100,18 @@ class AccountPresenter @Inject constructor(
}
private fun loadData() {
Timber.i("Loading account data started")
disposable.add(rxSingle { studentRepository.getSavedStudents(false) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.map { createAccountItems(it) }
.subscribe({
Timber.i("Loading account result: Success")
view?.updateData(it)
}, {
Timber.i("Loading account result: An exception occurred")
errorHandler.dispatch(it)
}))
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading account data started")
Status.SUCCESS -> {
Timber.i("Loading account result: Success")
view?.updateData(createAccountItems(it.data!!))
}
Status.ERROR -> {
Timber.i("Loading account result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch()
}
}

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.attendance
import android.annotation.SuppressLint
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@ -10,13 +11,18 @@ 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.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousOrSameSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
@ -168,100 +174,93 @@ class AttendancePresenter @Inject constructor(
}
private fun setBaseDateOnHolidays() {
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getCurrentSemester(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}) {
Timber.i("Loading semester result: An exception occurred")
})
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
Timber.i("Loading attendance data started")
currentDate = date
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { attendanceRepository.getAttendance(student, semester, date, date, forceRefresh) }
}
}
.map { list ->
if (prefRepository.isShowPresent) list
else list.filter { !it.presence }
}
.map { items -> items.sortedBy { it.number } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceRepository.getAttendance(student, semester, date, date, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> view?.showExcuseButton(false)
Status.SUCCESS -> {
Timber.i("Loading attendance result: Success")
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
updateData(it.data!!.let { items ->
if (prefRepository.isShowPresent) items
else items.filter { item -> !item.presence }
}.sortedBy { item -> item.number })
showEmpty(it.data.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
showExcuseButton(it.any { item -> item.excusable })
showContent(it.data.isNotEmpty())
showExcuseButton(it.data.any { item -> item.excusable })
}
analytics.logEvent(
"load_data",
"type" to "attendance",
"items" to it.size,
"force_refresh" to forceRefresh
"items" to it.data!!.size
)
}) {
Timber.i("Loading attendance result: An exception occurred")
errorHandler.dispatch(it)
}
)
}
Status.ERROR -> {
Timber.i("Loading attendance result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun excuseAbsence(reason: String?, toExcuseList: List<Attendance>) {
Timber.i("Excusing absence started")
disposable.apply {
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason) }
}
flowWithResource {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason)
}.onEach {
when (it.status) {
Status.LOADING -> view?.run {
Timber.i("Excusing absence started")
showProgress(true)
showContent(false)
showExcuseButton(false)
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.apply {
showProgress(true)
showContent(false)
showExcuseButton(false)
}
}
.subscribe({
Status.SUCCESS -> {
Timber.i("Excusing for absence result: Success")
analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size)
attendanceToExcuseList.clear()
view?.apply {
view?.run {
showExcuseButton(false)
showMessage(excuseSuccessString)
showContent(true)
showProgress(false)
}
loadData(currentDate, true)
}) {
loadData(currentDate, forceRefresh = true)
}
Status.ERROR -> {
Timber.i("Excusing for absence result: An exception occurred")
view?.showProgress(false)
errorHandler.dispatch(it)
})
}
errorHandler.dispatch(it.error!!)
loadData(currentDate)
}
}
}.launch("excuse")
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.attendance.summary
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
@ -9,7 +10,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 kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import org.threeten.bp.Month
import timber.log.Timber
import javax.inject.Inject
@ -73,46 +76,43 @@ class AttendanceSummaryPresenter @Inject constructor(
}
private fun loadData(subjectId: Int, forceRefresh: Boolean = false) {
Timber.i("Loading attendance summary data started")
currentSubjectId = subjectId
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap {
rxSingle { attendanceSummaryRepository.getAttendanceSummary(student, it, subjectId, forceRefresh) }
}
}
.map { items -> items.sortedByDescending { if (it.month.value <= Month.JUNE.value) it.month.value + 12 else it.month.value } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceSummaryRepository.getAttendanceSummary(student, semester, subjectId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading attendance summary data started")
Status.SUCCESS -> {
Timber.i("Loading attendance summary result: Success")
view?.apply {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
updateDataSet(it)
showEmpty(it.data!!.isEmpty())
showContent(it.data.isNotEmpty())
updateDataSet(it.data.sortedByDescending { item ->
if (item.month.value <= Month.JUNE.value) item.month.value + 12 else item.month.value
})
}
analytics.logEvent(
"load_data",
"type" to "attendance_summary",
"items" to it.size,
"force_refresh" to forceRefresh,
"items" to it.data!!.size,
"item_id" to subjectId
)
}) {
Timber.i("Loading attendance summary result: An exception occurred")
errorHandler.dispatch(it)
}
)
}
Status.ERROR -> {
Timber.i("Loading attendance summary result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@ -127,27 +127,27 @@ class AttendanceSummaryPresenter @Inject constructor(
}
private fun loadSubjects() {
Timber.i("Loading attendance summary subjects started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { subjectRepository.getSubjects(student, semester) }
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
subjectRepository.getSubjects(student, semester)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading attendance summary subjects started")
Status.SUCCESS -> {
subjects = it.data!!
Timber.i("Loading attendance summary subjects result: Success")
view?.run {
view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name }))
showSubjects(true)
}
}
Status.ERROR -> {
Timber.i("Loading attendance summary subjects result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.doOnSuccess { subjects = it }
.map { ArrayList(it.map { subject -> subject.name }) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Loading attendance summary subjects result: Success")
view?.run {
view?.updateSubjects(it)
showSubjects(true)
}
}, {
Timber.i("Loading attendance summary subjects result: An exception occurred")
errorHandler.dispatch(it)
})
)
}.launch("subjects")
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.exam
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.repositories.exam.ExamRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
@ -8,13 +9,17 @@ 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.sunday
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
@ -90,59 +95,54 @@ class ExamPresenter @Inject constructor(
}
private fun setBaseDateOnHolidays() {
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getCurrentSemester(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}) {
Timber.i("Loading semester result: An exception occurred")
})
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
Timber.i("Loading exam data started")
currentDate = date
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { examRepository.getExams(student, semester, currentDate.monday, currentDate.sunday, forceRefresh) }
}
}
.map { createExamItems(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
examRepository.getExams(student, semester, currentDate.monday, currentDate.sunday, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading exam data started")
Status.SUCCESS -> {
Timber.i("Loading exam result: Success")
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
updateData(createExamItems(it.data!!))
showEmpty(it.data.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
showContent(it.data.isNotEmpty())
}
analytics.logEvent(
"load_data",
"type" to "exam",
"items" to it.size,
"force_refresh" to forceRefresh
"items" to it.data!!.size
)
}) {
}
Status.ERROR -> {
Timber.i("Loading exam result: An exception occurred")
errorHandler.dispatch(it)
})
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
@ -13,8 +15,17 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.BOTH_SEMESTERS
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ONE_SEMESTER
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@OptIn(FlowPreview::class)
class GradeAverageProvider @Inject constructor(
private val semesterRepository: SemesterRepository,
private val gradeRepository: GradeRepository,
@ -25,63 +36,64 @@ class GradeAverageProvider @Inject constructor(
private val minusModifier get() = preferencesRepository.gradeMinusModifier
suspend fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean = false): List<GradeDetailsWithAverage> {
return semesterRepository.getSemesters(student).let { semesters ->
when (preferencesRepository.gradeAverageMode) {
ONE_SEMESTER -> getSemesterDetailsWithAverage(student, semesters.single { it.semesterId == semesterId }, forceRefresh)
BOTH_SEMESTERS -> calculateBothSemestersAverage(student, semesters, semesterId, forceRefresh)
ALL_YEAR -> calculateAllYearAverage(student, semesters, semesterId, forceRefresh)
}
}
}
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) = flowWithResourceIn {
val semesters = semesterRepository.getSemesters(student)
private suspend fun calculateBothSemestersAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): List<GradeDetailsWithAverage> {
when (preferencesRepository.gradeAverageMode) {
ONE_SEMESTER -> getSemesterDetailsWithAverage(student, semesters.single { it.semesterId == semesterId }, forceRefresh)
BOTH_SEMESTERS -> calculateBothSemestersAverage(student, semesters, semesterId, forceRefresh)
ALL_YEAR -> calculateAllYearAverage(student, semesters, semesterId, forceRefresh)
}
}.distinctUntilChanged()
private fun calculateBothSemestersAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
val selectedSemester = semesters.single { it.semesterId == semesterId }
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).let { selectedDetails ->
val isAnyAverage = selectedDetails.any { it.average != .0 }
return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).flatMapConcat { selectedDetails ->
val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 }
if (selectedSemester != firstSemester) {
getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).let { secondDetails ->
selectedDetails.map { selected ->
val second = secondDetails.singleOrNull { it.subject == selected.subject }
getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).map { secondDetails ->
secondDetails.copy(data = selectedDetails.data?.map { selected ->
val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject }
selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
val selectedGrades = selected.grades.updateModifiers(student).calcAverage()
(selectedGrades + (second?.grades?.updateModifiers(student)?.calcAverage() ?: selectedGrades)) / 2
} else (selected.average + (second?.average ?: selected.average)) / 2)
}
}
} else selectedDetails
})
}.filter { it.status != Status.LOADING }.filter { it.data != null }
} else flowOf(selectedDetails)
}
}
private suspend fun calculateAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): List<GradeDetailsWithAverage> {
private fun calculateAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
val selectedSemester = semesters.single { it.semesterId == semesterId }
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).let { selectedDetails ->
val isAnyAverage = selectedDetails.any { it.average != .0 }
return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).flatMapConcat { selectedDetails ->
val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 }
if (selectedSemester != firstSemester) {
getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).let { secondDetails ->
selectedDetails.map { selected ->
val second = secondDetails.singleOrNull { it.subject == selected.subject }
getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).map { secondDetails ->
secondDetails.copy(data = selectedDetails.data?.map { selected ->
val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject }
selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
(selected.grades.updateModifiers(student) + second?.grades?.updateModifiers(student).orEmpty()).calcAverage()
} else selected.average)
}
}
} else selectedDetails
})
}.filter { it.status != Status.LOADING }.filter { it.data != null }
} else flowOf(selectedDetails)
}
}
private suspend fun getSemesterDetailsWithAverage(student: Student, semester: Semester, forceRefresh: Boolean): List<GradeDetailsWithAverage> {
return gradeRepository.getGrades(student, semester, forceRefresh).let { (details, summaries) ->
val isAnyAverage = summaries.any { it.average != .0 }
val allGrades = details.groupBy { it.subject }
private fun getSemesterDetailsWithAverage(student: Student, semester: Semester, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh).map { res ->
val (details, summaries) = res.data ?: null to null
val isAnyAverage = summaries.orEmpty().any { it.average != .0 }
val allGrades = details.orEmpty().groupBy { it.subject }
summaries.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage).map { summary ->
Resource(res.status, summaries?.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage)?.map { summary ->
val grades = allGrades[summary.subject].orEmpty()
GradeDetailsWithAverage(
subject = summary.subject,
@ -92,7 +104,7 @@ class GradeAverageProvider @Inject constructor(
summary = summary,
grades = grades
)
}
}, res.error)
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
@ -7,10 +8,11 @@ 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.flowWithResource
import io.github.wulkanowy.utils.getCurrentOrLast
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class GradePresenter @Inject constructor(
@ -99,29 +101,33 @@ class GradePresenter @Inject constructor(
}
private fun loadData() {
Timber.i("Loading grade data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getSemesters(it, refreshOnNoCurrent = true) } }
.delay(200, MILLISECONDS)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
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)
flowWithResource {
val student = studentRepository.getCurrentStudent()
delay(200)
semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade data started")
Status.SUCCESS -> {
val current = it.data!!.getCurrentOrLast()
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
schoolYear = current.schoolYear
semesters = it.data.filter { semester -> semester.diaryId == current.diaryId }
view?.setCurrentSemesterName(current.semesterName, schoolYear)
view?.run {
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
loadChild(currentPageIndex)
showErrorView(false)
showSemesterSwitch(true)
view?.run {
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
loadChild(currentPageIndex)
showErrorView(false)
showSemesterSwitch(true)
}
}
}) {
Timber.i("Loading grade result: An exception occurred")
errorHandler.dispatch(it)
})
Status.ERROR -> {
Timber.i("Loading grade result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.details
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@ -11,8 +12,11 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -41,7 +45,9 @@ class GradeDetailsPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
currentSemesterId = semesterId
loadData(semesterId, forceRefresh)
if (!forceRefresh) view?.showErrorView(false)
}
fun onGradeItemSelected(grade: Grade, position: Int) {
@ -63,24 +69,24 @@ class GradeDetailsPresenter @Inject constructor(
}
fun onMarkAsReadSelected(): Boolean {
Timber.i("Select mark grades as read")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getSemesters(it) } }
.flatMap { rxSingle { gradeRepository.getUnreadGrades(it.first { item -> item.semesterId == currentSemesterId }) } }
.map { it.map { grade -> grade.apply { isRead = true } } }
.flatMapCompletable {
Timber.i("Mark as read ${it.size} grades")
rxCompletable { gradeRepository.updateGrades(it) }
flowWithResource {
val student = studentRepository.getCurrentStudent()
val semesters = semesterRepository.getSemesters(student)
val semester = semesters.first { item -> item.semesterId == currentSemesterId }
val unreadGrades = gradeRepository.getUnreadGrades(semester).first()
Timber.i("Mark as read ${unreadGrades.size} grades")
gradeRepository.updateGrades(unreadGrades.map { it.apply { isRead = true } })
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Select mark grades as read")
Status.SUCCESS -> Timber.i("Mark as read result: Success")
Status.ERROR -> {
Timber.i("Mark as read result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Mark as read result: Success")
loadData(currentSemesterId, false)
}, {
Timber.i("Mark as read result: An exception occurred")
errorHandler.dispatch(it)
}))
}.launch("mark")
return true
}
@ -119,7 +125,7 @@ class GradeDetailsPresenter @Inject constructor(
showEmpty(false)
clearView()
}
disposable.clear()
cancelJobs("load")
}
fun updateMarkAsDoneButton() {
@ -127,43 +133,46 @@ class GradeDetailsPresenter @Inject constructor(
}
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade details data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { averageProvider.getGradesDetailsWithAverage(it, semesterId, forceRefresh) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
}
.subscribe({ grades ->
Timber.i("Loading grade details result: Success")
newGradesAmount = grades.sumBy { it.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
updateMarkAsDoneButton()
view?.run {
showEmpty(grades.isEmpty())
showErrorView(false)
showContent(grades.isNotEmpty())
updateData(
data = createGradeItems(grades),
isGradeExpandable = preferencesRepository.isGradeExpandable,
gradeColorTheme = preferencesRepository.gradeColorTheme
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade details data started")
Status.SUCCESS -> {
Timber.i("Loading grade details result: Success")
newGradesAmount = it.data!!.sumBy { item -> item.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
updateMarkAsDoneButton()
val items = createGradeItems(it.data)
view?.run {
showEmpty(items.isEmpty())
showErrorView(false)
showContent(items.isNotEmpty())
updateData(
data = items,
isGradeExpandable = preferencesRepository.isGradeExpandable,
gradeColorTheme = preferencesRepository.gradeColorTheme
)
}
analytics.logEvent(
"load_data",
"type" to "grade_details",
"items" to it.data.size
)
}
analytics.logEvent(
"load_data",
"type" to "grade_details",
"items" to grades.size,
"force_refresh" to forceRefresh
)
}) {
Timber.i("Loading grade details result: An exception occurred")
errorHandler.dispatch(it)
})
Status.ERROR -> {
Timber.i("Loading grade details result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@ -197,15 +206,15 @@ class GradeDetailsPresenter @Inject constructor(
}
private fun updateGrade(grade: Grade) {
Timber.i("Attempt to update grade ${grade.id}")
disposable.add(rxCompletable { gradeRepository.updateGrade(grade) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Update grade result: Success")
}) { error ->
Timber.i("Update grade result: An exception occurred")
errorHandler.dispatch(error)
})
flowWithResource { gradeRepository.updateGrade(grade) }.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to update grade ${grade.id}")
Status.SUCCESS -> Timber.i("Update grade result: Success")
Status.ERROR -> {
Timber.i("Update grade result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch("update")
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.statistics
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@ -10,7 +11,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 kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -46,6 +49,7 @@ class GradeStatisticsPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
currentSemesterId = semesterId
loadSubjects()
if (!forceRefresh) view?.showErrorView(false)
loadDataByType(semesterId, currentSubjectName, currentType, forceRefresh)
}
@ -65,7 +69,7 @@ class GradeStatisticsPresenter @Inject constructor(
showEmpty(false)
clearView()
}
disposable.clear()
cancelJobs("load")
}
fun onSwipeRefresh() {
@ -103,7 +107,7 @@ class GradeStatisticsPresenter @Inject constructor(
fun onTypeChange() {
val type = view?.currentType ?: ViewType.POINTS
Timber.i("Select grade stats semester: $type")
disposable.clear()
cancelJobs("load")
view?.run {
showContent(false)
showProgress(true)
@ -116,77 +120,77 @@ class GradeStatisticsPresenter @Inject constructor(
}
private fun loadSubjects() {
Timber.i("Loading grade stats subjects started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { subjectRepository.getSubjects(student, semester) }
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
subjectRepository.getSubjects(student, semester)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade stats subjects started")
Status.SUCCESS -> {
subjects = it.data!!
Timber.i("Loading grade stats subjects result: Success")
view?.run {
view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name }))
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
}
Status.ERROR -> {
Timber.i("Loading grade stats subjects result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.doOnSuccess { subjects = it }
.map { ArrayList(it.map { subject -> subject.name }) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Loading grade stats subjects result: Success")
view?.updateSubjects(it)
}, {
Timber.i("Loading grade stats subjects result: An exception occurred")
errorHandler.dispatch(it)
})
)
}.launch("subjects")
}
private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) {
currentSubjectName = if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName
currentType = type
Timber.i("Loading grade stats data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getSemesters(student) }.flatMap { semesters ->
val semester = semesters.first { item -> item.semesterId == semesterId }
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semesters = semesterRepository.getSemesters(student)
val semester = semesters.first { item -> item.semesterId == semesterId }
rxSingle {
with(gradeStatisticsRepository) {
when (type) {
ViewType.SEMESTER -> getGradesStatistics(student, semester, currentSubjectName, true, forceRefresh)
ViewType.PARTIAL -> getGradesStatistics(student, semester, currentSubjectName, false, forceRefresh)
ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh)
}
}
with(gradeStatisticsRepository) {
when (type) {
ViewType.SEMESTER -> getGradesStatistics(student, semester, currentSubjectName, true, forceRefresh)
ViewType.PARTIAL -> getGradesStatistics(student, semester, currentSubjectName, false, forceRefresh)
ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh)
}
}
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade stats data started")
Status.SUCCESS -> {
Timber.i("Loading grade stats result: Success")
view?.run {
showEmpty(it.data!!.isEmpty())
showContent(it.data.isNotEmpty())
showErrorView(false)
updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
analytics.logEvent(
"load_data",
"type" to "grade_statistics",
"items" to it.data!!.size
)
}
Status.ERROR -> {
Timber.i("Loading grade stats result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
}.afterLoading {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
.subscribe({
Timber.i("Loading grade stats result: Success")
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
showErrorView(false)
updateData(it, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
analytics.logEvent(
"load_data",
"type" to "grade_statistics",
"items" to it.size,
"force_refresh" to forceRefresh
)
}) {
Timber.i("Loading grade stats result: An exception occurred")
errorHandler.dispatch(it)
})
}.launch("load")
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@ -8,7 +9,9 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -30,36 +33,45 @@ class GradeSummaryPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade summary data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { averageProvider.getGradesDetailsWithAverage(it, semesterId, forceRefresh) } }
.map { createGradeSummaryItems(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
loadData(semesterId, forceRefresh)
if (!forceRefresh) view?.showErrorView(false)
}
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade summary started")
Status.SUCCESS -> {
Timber.i("Loading grade summary result: Success")
view?.run {
showEmpty(it.data!!.isEmpty())
showContent(it.data.isNotEmpty())
showErrorView(false)
updateData(createGradeSummaryItems(it.data))
}
analytics.logEvent(
"load_data",
"type" to "grade_summary",
"items" to it.data!!.size
)
}
}.subscribe({
Timber.i("Loading grade summary result: Success")
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
showErrorView(false)
updateData(it)
Status.ERROR -> {
Timber.i("Loading grade summary result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
analytics.logEvent(
"load_data",
"type" to "grade_summary",
"items" to it.size,
"force_refresh" to forceRefresh
)
}) {
Timber.i("Loading grade summary result: An exception occurred")
errorHandler.dispatch(it)
})
}
}.afterLoading {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@ -105,7 +117,7 @@ class GradeSummaryPresenter @Inject constructor(
showEmpty(false)
clearView()
}
disposable.clear()
cancelJobs("load")
}
private fun createGradeSummaryItems(items: List<GradeDetailsWithAverage>): List<GradeSummary> {

View File

@ -70,10 +70,6 @@ class HomeworkFragment : BaseFragment<FragmentHomeworkBinding>(R.layout.fragment
}
}
fun onReloadList() {
presenter.reloadData()
}
override fun clearData() {
with(homeworkAdapter) {
items = emptyList()

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.homework
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
@ -8,13 +9,17 @@ 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.sunday
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.ofEpochDay
import timber.log.Timber
@ -79,64 +84,54 @@ class HomeworkPresenter @Inject constructor(
}
private fun setBaseDateOnHolidays() {
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getCurrentSemester(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}) {
Timber.i("Loading semester result: An exception occurred")
})
}
fun reloadData() {
loadData(currentDate, false)
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
Timber.i("Loading homework data started")
currentDate = date
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { homeworkRepository.getHomework(student, semester, currentDate, currentDate, forceRefresh) }
}
}
.map { createHomeworkItem(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
homeworkRepository.getHomework(student, semester, date, date, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading homework data started")
Status.SUCCESS -> {
Timber.i("Loading homework result: Success")
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
updateData(createHomeworkItem(it.data!!))
showEmpty(it.data.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
showContent(it.data.isNotEmpty())
}
analytics.logEvent(
"load_data",
"type" to "homework",
"items" to it.size,
"force_refresh" to forceRefresh
"items" to it.data!!.size
)
}) {
}
Status.ERROR -> {
Timber.i("Loading homework result: An exception occurred")
errorHandler.dispatch(it)
})
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -73,7 +73,6 @@ class HomeworkDetailsDialog : BaseDialogFragment<DialogHomeworkBinding>(), Homew
}
override fun updateMarkAsDoneLabel(isDone: Boolean) {
(parentFragment as? HomeworkFragment)?.onReloadList()
binding.homeworkDialogRead.text = view?.context?.getString(if (isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done)
}

Some files were not shown because too many files have changed in this diff Show More