Optimize grade average provider (#792)

This commit is contained in:
Mikołaj Pich 2020-05-10 10:51:01 +02:00 committed by GitHub
parent 8eb0c0351b
commit 6d1fa0cf05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 278 additions and 324 deletions

View File

@ -122,7 +122,7 @@ configurations.all {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:sdk:0.17.4" implementation "io.github.wulkanowy:sdk:445905b"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0" implementation "androidx.core:core-ktx:1.2.0"

View File

@ -24,7 +24,7 @@ class GradeLocalTest {
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build() .build()
gradeLocal = GradeLocal(testDb.gradeDao) gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao)
} }
@After @After
@ -43,7 +43,7 @@ class GradeLocalTest {
val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1) val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1)
val grades = gradeLocal val grades = gradeLocal
.getGrades(semester) .getGradesDetails(semester)
.blockingGet() .blockingGet()
assertEquals(2, grades.size) assertEquals(2, grades.size)

View File

@ -11,6 +11,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Grade
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
@ -52,7 +53,7 @@ class GradeRepositoryTest {
fun initApi() { fun initApi() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
gradeLocal = GradeLocal(testDb.gradeDao) gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao)
gradeRemote = GradeRemote(mockSdk) gradeRemote = GradeRemote(mockSdk)
every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0) every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0)
@ -75,10 +76,10 @@ class GradeRepositoryTest {
createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), 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, 27), "Ocena z dnia logowania"),
createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza") createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date } .getGrades(studentMock, semesterMock, true).blockingGet().first.sortedByDescending { it.date }
assertFalse { grades[0].isRead } assertFalse { grades[0].isRead }
assertFalse { grades[1].isRead } assertFalse { grades[1].isRead }
@ -99,10 +100,10 @@ class GradeRepositoryTest {
createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), 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(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") createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date } .getGrades(studentMock, semesterMock, true).blockingGet().first.sortedByDescending { it.date }
assertFalse { grades[0].isRead } assertFalse { grades[0].isRead }
assertFalse { grades[1].isRead } assertFalse { grades[1].isRead }
@ -121,12 +122,12 @@ class GradeRepositoryTest {
every { mockSdk.getGrades(1) } returns Single.just(listOf( every { mockSdk.getGrades(1) } returns Single.just(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") createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet() .getGrades(studentMock, semesterMock, true).blockingGet()
assertEquals(2, grades.size) assertEquals(2, grades.first.size)
} }
@Test @Test
@ -140,12 +141,12 @@ class GradeRepositoryTest {
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
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") createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet() .getGrades(studentMock, semesterMock, true).blockingGet()
assertEquals(3, grades.size) assertEquals(3, grades.first.size)
} }
@Test @Test
@ -156,12 +157,12 @@ class GradeRepositoryTest {
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
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") createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet() .getGrades(studentMock, semesterMock, true).blockingGet()
assertEquals(3, grades.size) assertEquals(3, grades.first.size)
} }
@Test @Test
@ -171,11 +172,11 @@ class GradeRepositoryTest {
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) ))
every { mockSdk.getGrades(1) } returns Single.just(listOf()) every { mockSdk.getGrades(1) } returns Single.just(emptyList<Grade>() to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet() .getGrades(studentMock, semesterMock, true).blockingGet()
assertEquals(0, grades.size) assertEquals(0, grades.first.size)
} }
} }

View File

@ -1,14 +1,19 @@
package io.github.wulkanowy.data.repositories.grade package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.data.db.dao.GradeDao import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.Grade 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.Semester
import io.reactivex.Maybe import io.reactivex.Maybe
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class GradeLocal @Inject constructor(private val gradeDb: GradeDao) { class GradeLocal @Inject constructor(
private val gradeDb: GradeDao,
private val gradeSummaryDb: GradeSummaryDao
) {
fun saveGrades(grades: List<Grade>) { fun saveGrades(grades: List<Grade>) {
gradeDb.insertAll(grades) gradeDb.insertAll(grades)
@ -22,7 +27,19 @@ class GradeLocal @Inject constructor(private val gradeDb: GradeDao) {
gradeDb.updateAll(grades) gradeDb.updateAll(grades)
} }
fun getGrades(semester: Semester): Maybe<List<Grade>> { fun getGradesDetails(semester: Semester): Maybe<List<Grade>> {
return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
} }
fun saveGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.insertAll(gradesSummary)
}
fun deleteGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.deleteAll(gradesSummary)
}
fun getGradesSummary(semester: Semester): Maybe<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
}
} }

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.data.repositories.grade package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.data.db.entities.Grade 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
@ -12,11 +13,11 @@ import javax.inject.Singleton
@Singleton @Singleton
class GradeRemote @Inject constructor(private val sdk: Sdk) { class GradeRemote @Inject constructor(private val sdk: Sdk) {
fun getGrades(student: Student, semester: Semester): Single<List<Grade>> { fun getGrades(student: Student, semester: Semester): Single<Pair<List<Grade>, List<GradeSummary>>> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getGrades(semester.semesterId) .getGrades(semester.semesterId)
.map { grades -> .map { (details, summary) ->
grades.map { details.map {
Grade( Grade(
studentId = semester.studentId, studentId = semester.studentId,
semesterId = semester.semesterId, semesterId = semester.semesterId,
@ -33,6 +34,19 @@ class GradeRemote @Inject constructor(private val sdk: Sdk) {
date = it.date, date = it.date,
teacher = it.teacher teacher = it.teacher
) )
} to summary.map {
GradeSummary(
semesterId = semester.semesterId,
studentId = semester.studentId,
position = 0,
subject = it.name,
predictedGrade = it.predicted,
finalGrade = it.final,
pointsSum = it.pointsSum,
proposedPoints = it.proposedPoints,
finalPoints = it.finalPoints,
average = it.average
)
} }
} }
} }

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.grade
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Grade 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
@ -19,34 +20,47 @@ class GradeRepository @Inject constructor(
private val remote: GradeRemote private val remote: GradeRemote
) { ) {
fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single<List<Grade>> { fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single<Pair<List<Grade>, List<GradeSummary>>> {
return local.getGrades(semester).filter { !forceRefresh } return local.getGradesDetails(semester).flatMap { details ->
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) local.getGradesSummary(semester).map { summary -> details to summary }
.flatMap { }.filter { !forceRefresh }
if (it) remote.getGrades(student, semester) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
else Single.error(UnknownHostException()) if (it) remote.getGrades(student, semester)
}.flatMap { new -> else Single.error(UnknownHostException())
local.getGrades(semester).toSingle(emptyList()) }.flatMap { (newDetails, newSummary) ->
.doOnSuccess { old -> local.getGradesDetails(semester).toSingle(emptyList())
val notifyBreakDate = old.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate() .doOnSuccess { old ->
local.deleteGrades(old.uniqueSubtract(new)) val notifyBreakDate = old.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate()
local.saveGrades(new.uniqueSubtract(old) local.deleteGrades(old.uniqueSubtract(newDetails))
.onEach { local.saveGrades(newDetails.uniqueSubtract(old)
if (it.date >= notifyBreakDate) it.apply { .onEach {
isRead = false if (it.date >= notifyBreakDate) it.apply {
if (notify) isNotified = false isRead = false
} if (notify) isNotified = false
}) }
} })
}.flatMap { local.getGrades(semester).toSingle(emptyList()) }) }.flatMap {
local.getGradesSummary(semester).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteGradesSummary(old.uniqueSubtract(newSummary))
local.saveGradesSummary(newSummary.uniqueSubtract(old))
}
}
}.flatMap {
local.getGradesDetails(semester).toSingle(emptyList()).flatMap { details ->
local.getGradesSummary(semester).toSingle(emptyList()).map { summary ->
details to summary
}
}
})
} }
fun getUnreadGrades(semester: Semester): Single<List<Grade>> { fun getUnreadGrades(semester: Semester): Single<List<Grade>> {
return local.getGrades(semester).map { it.filter { grade -> !grade.isRead } }.toSingle(emptyList()) return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isRead } }.toSingle(emptyList())
} }
fun getNotNotifiedGrades(semester: Semester): Single<List<Grade>> { fun getNotNotifiedGrades(semester: Semester): Single<List<Grade>> {
return local.getGrades(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList()) return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList())
} }
fun updateGrade(grade: Grade): Completable { fun updateGrade(grade: Grade): Completable {

View File

@ -1,24 +0,0 @@
package io.github.wulkanowy.data.repositories.gradessummary
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) {
fun saveGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.insertAll(gradesSummary)
}
fun deleteGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.deleteAll(gradesSummary)
}
fun getGradesSummary(semester: Semester): Maybe<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
}
}

View File

@ -1,35 +0,0 @@
package io.github.wulkanowy.data.repositories.gradessummary
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.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeSummaryRemote @Inject constructor(private val sdk: Sdk) {
fun getGradeSummary(student: Student, semester: Semester): Single<List<GradeSummary>> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getGradesSummary(semester.semesterId)
.map { gradesSummary ->
gradesSummary.map {
GradeSummary(
semesterId = semester.semesterId,
studentId = semester.studentId,
position = 0,
subject = it.name,
predictedGrade = it.predicted,
finalGrade = it.final,
pointsSum = it.pointsSum,
proposedPoints = it.proposedPoints,
finalPoints = it.finalPoints,
average = it.average
)
}
}
}
}

View File

@ -1,35 +0,0 @@
package io.github.wulkanowy.data.repositories.gradessummary
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
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.uniqueSubtract
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeSummaryRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: GradeSummaryLocal,
private val remote: GradeSummaryRemote
) {
fun getGradesSummary(student: Student, semester: Semester, forceRefresh: Boolean = false): Single<List<GradeSummary>> {
return local.getGradesSummary(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getGradeSummary(student, semester)
else Single.error(UnknownHostException())
}.flatMap { new ->
local.getGradesSummary(semester).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteGradesSummary(old.uniqueSubtract(new))
local.saveGradesSummary(new.uniqueSubtract(old))
}
}.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) })
}
}

View File

@ -21,7 +21,6 @@ import io.github.wulkanowy.services.sync.works.AttendanceWork
import io.github.wulkanowy.services.sync.works.CompletedLessonWork import io.github.wulkanowy.services.sync.works.CompletedLessonWork
import io.github.wulkanowy.services.sync.works.ExamWork import io.github.wulkanowy.services.sync.works.ExamWork
import io.github.wulkanowy.services.sync.works.GradeStatisticsWork import io.github.wulkanowy.services.sync.works.GradeStatisticsWork
import io.github.wulkanowy.services.sync.works.GradeSummaryWork
import io.github.wulkanowy.services.sync.works.GradeWork import io.github.wulkanowy.services.sync.works.GradeWork
import io.github.wulkanowy.services.sync.works.HomeworkWork import io.github.wulkanowy.services.sync.works.HomeworkWork
import io.github.wulkanowy.services.sync.works.LuckyNumberWork import io.github.wulkanowy.services.sync.works.LuckyNumberWork
@ -64,10 +63,6 @@ abstract class ServicesModule {
@IntoSet @IntoSet
abstract fun provideAttendanceWork(work: AttendanceWork): Work abstract fun provideAttendanceWork(work: AttendanceWork): Work
@Binds
@IntoSet
abstract fun provideGradeSummaryWork(work: GradeSummaryWork): Work
@Binds @Binds
@IntoSet @IntoSet
abstract fun provideExamWork(work: ExamWork): Work abstract fun provideExamWork(work: ExamWork): Work

View File

@ -1,14 +0,0 @@
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.gradessummary.GradeSummaryRepository
import io.reactivex.Completable
import javax.inject.Inject
class GradeSummaryWork @Inject constructor(private val gradeSummaryRepository: GradeSummaryRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return gradeSummaryRepository.getGradesSummary(student, semester, true).ignoreElement()
}
}

View File

@ -58,4 +58,3 @@ class GradeWork @Inject constructor(
) )
} }
} }

View File

@ -3,71 +3,76 @@ package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier import io.github.wulkanowy.utils.changeModifier
import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
class GradeAverageProvider @Inject constructor( class GradeAverageProvider @Inject constructor(
private val preferencesRepository: PreferencesRepository, private val semesterRepository: SemesterRepository,
private val gradeRepository: GradeRepository, private val gradeRepository: GradeRepository,
private val gradeSummaryRepository: GradeSummaryRepository private val preferencesRepository: PreferencesRepository
) { ) {
private val plusModifier = preferencesRepository.gradePlusModifier private val plusModifier = preferencesRepository.gradePlusModifier
private val minusModifier = preferencesRepository.gradeMinusModifier private val minusModifier = preferencesRepository.gradeMinusModifier
fun getGradeAverage(student: Student, semesters: List<Semester>, selectedSemesterId: Int, forceRefresh: Boolean): Single<List<Triple<String, Double, String>>> { fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean = false): Single<List<GradeDetailsWithAverage>> {
return when (preferencesRepository.gradeAverageMode) { return semesterRepository.getSemesters(student).flatMap { semesters ->
"all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh) when (preferencesRepository.gradeAverageMode) {
"only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh) "only_one_semester" -> getSemesterDetailsWithAverage(student, semesters.single { it.semesterId == semesterId }, forceRefresh)
else -> throw IllegalArgumentException("Incorrect grade average mode: ${preferencesRepository.gradeAverageMode} ") "all_year" -> calculateWholeYearAverage(student, semesters, semesterId, forceRefresh)
else -> throw IllegalArgumentException("Incorrect grade average mode: ${preferencesRepository.gradeAverageMode} ")
}
} }
} }
private fun getAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<List<Triple<String, Double, String>>> { private fun calculateWholeYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<List<GradeDetailsWithAverage>> {
val selectedSemester = semesters.single { it.semesterId == semesterId } val selectedSemester = semesters.single { it.semesterId == semesterId }
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
return getAverageFromGradeSummary(student, selectedSemester, forceRefresh) return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).flatMap { selectedDetails ->
.switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) val isAnyAverage = selectedDetails.any { it.average != .0 }
.flatMap { firstGrades ->
if (selectedSemester == firstSemester) Single.just(firstGrades) if (selectedSemester != firstSemester) {
else { getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).map { secondDetails ->
gradeRepository.getGrades(student, firstSemester) selectedDetails.map { selected ->
.map { secondGrades -> secondGrades + firstGrades } val second = secondDetails.singleOrNull { it.subject == selected.subject }
selected.copy(
average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
(selected.grades + second?.grades.orEmpty()).calcAverage()
} else (selected.average + (second?.average ?: selected.average)) / 2
)
} }
}.map { grades -> }
grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it } } else Single.just(selectedDetails)
.groupBy { it.subject } }
.map { Triple(it.key, it.value.calcAverage(), "") }
})
} }
private fun getOnlyOneSemesterAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<List<Triple<String, Double, String>>> { private fun getSemesterDetailsWithAverage(student: Student, semester: Semester, forceRefresh: Boolean): Single<List<GradeDetailsWithAverage>> {
val selectedSemester = semesters.single { it.semesterId == semesterId } return gradeRepository.getGrades(student, semester, forceRefresh).map { (details, summaries) ->
val isAnyAverage = summaries.any { it.average != .0 }
val allGrades = details.groupBy { it.subject }
return getAverageFromGradeSummary(student, selectedSemester, forceRefresh) summaries.map { summary ->
.switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) val grades = allGrades[summary.subject].orEmpty()
.map { grades -> GradeDetailsWithAverage(
grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it } subject = summary.subject,
.groupBy { it.subject } average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
.map { Triple(it.key, it.value.calcAverage(), "") } grades.map {
}) if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier)
} else it
}.calcAverage()
private fun getAverageFromGradeSummary(student: Student, selectedSemester: Semester, forceRefresh: Boolean): Maybe<List<Triple<String, Double, String>>> { } else summary.average,
return gradeSummaryRepository.getGradesSummary(student, selectedSemester, forceRefresh) points = summary.pointsSum,
.toMaybe() summary = summary,
.flatMap { grades = grades
if (it.any { summary -> summary.average != .0 }) { )
Maybe.just(it.map { summary -> Triple(summary.subject, summary.average, summary.pointsSum) }) }
} else Maybe.empty() }
}.filter { !preferencesRepository.gradeAverageForceCalc }
} }
} }

View File

@ -0,0 +1,12 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
data class GradeDetailsWithAverage(
val subject: String,
val average: Double,
val points: String,
val summary: GradeSummary,
val grades: List<Grade>
)

View File

@ -107,7 +107,7 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
gradeHeaderAverage.text = formatAverage(header.average, root.context.resources) gradeHeaderAverage.text = formatAverage(header.average, root.context.resources)
gradeHeaderPointsSum.text = root.context.getString(R.string.grade_points_sum, header.pointsSum) gradeHeaderPointsSum.text = root.context.getString(R.string.grade_points_sum, header.pointsSum)
gradeHeaderPointsSum.visibility = if (!header.pointsSum.isNullOrEmpty()) View.VISIBLE else View.GONE gradeHeaderPointsSum.visibility = if (!header.pointsSum.isNullOrEmpty()) View.VISIBLE else View.GONE
gradeHeaderNumber.text = root.context.resources.getQuantityString(R.plurals.grade_number_item, header.number, header.number) gradeHeaderNumber.text = root.context.resources.getQuantityString(R.plurals.grade_number_item, header.grades.size, header.grades.size)
gradeHeaderNote.visibility = if (header.newGrades > 0) View.VISIBLE else View.GONE gradeHeaderNote.visibility = if (header.newGrades > 0) View.VISIBLE else View.GONE
if (header.newGrades > 0) gradeHeaderNote.text = header.newGrades.toString(10) if (header.newGrades > 0) gradeHeaderNote.text = header.newGrades.toString(10)

View File

@ -12,7 +12,6 @@ data class GradeDetailsItem(
data class GradeDetailsHeader( data class GradeDetailsHeader(
val subject: String, val subject: String,
val number: Int,
val average: Double?, val average: Double?,
val pointsSum: String?, val pointsSum: String?,
var newGrades: Int, var newGrades: Int,

View File

@ -8,6 +8,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider 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.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber import timber.log.Timber
@ -126,14 +127,7 @@ class GradeDetailsPresenter @Inject constructor(
private fun loadData(semesterId: Int, forceRefresh: Boolean) { private fun loadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade details data started") Timber.i("Loading grade details data started")
disposable.add(studentRepository.getCurrentStudent() disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } } .flatMap { averageProvider.getGradesDetailsWithAverage(it, semesterId, forceRefresh) }
.flatMap { (student, semesters) ->
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh).flatMap { averages ->
gradeRepository.getGrades(student, semesters.first { it.semesterId == semesterId }, forceRefresh)
.map { it.sortedByDescending { grade -> grade.date } }
.map { createGradeItems(it, averages) }
}
}
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doFinally { .doFinally {
@ -146,16 +140,14 @@ class GradeDetailsPresenter @Inject constructor(
} }
.subscribe({ grades -> .subscribe({ grades ->
Timber.i("Loading grade details result: Success") Timber.i("Loading grade details result: Success")
newGradesAmount = grades newGradesAmount = grades.sumBy { it.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
.filter { it.viewType == ViewType.HEADER }
.sumBy { item -> (item.value as GradeDetailsHeader).newGrades }
updateMarkAsDoneButton() updateMarkAsDoneButton()
view?.run { view?.run {
showEmpty(grades.isEmpty()) showEmpty(grades.isEmpty())
showErrorView(false) showErrorView(false)
showContent(grades.isNotEmpty()) showContent(grades.isNotEmpty())
updateData( updateData(
data = grades, data = createGradeItems(grades),
isGradeExpandable = preferencesRepository.isGradeExpandable, isGradeExpandable = preferencesRepository.isGradeExpandable,
gradeColorTheme = preferencesRepository.gradeColorTheme gradeColorTheme = preferencesRepository.gradeColorTheme
) )
@ -178,17 +170,16 @@ class GradeDetailsPresenter @Inject constructor(
} }
} }
private fun createGradeItems(items: List<Grade>, averages: List<Triple<String, Double, String>>): List<GradeDetailsItem> { private fun createGradeItems(items: List<GradeDetailsWithAverage>): List<GradeDetailsItem> {
return items.groupBy { grade -> grade.subject }.toSortedMap().map { (subject, grades) -> return items.filter { it.grades.isNotEmpty() }.map { (subject, average, points, _, grades) ->
val subItems = grades.map { val subItems = grades.map {
GradeDetailsItem(it, ViewType.ITEM) GradeDetailsItem(it, ViewType.ITEM)
} }
listOf(GradeDetailsItem(GradeDetailsHeader( listOf(GradeDetailsItem(GradeDetailsHeader(
subject = subject, subject = subject,
average = averages.singleOrNull { subject == it.first }?.second, average = average,
pointsSum = averages.singleOrNull { subject == it.first }?.third, pointsSum = points,
number = grades.size,
newGrades = grades.filter { grade -> !grade.isRead }.size, newGrades = grades.filter { grade -> !grade.isRead }.size,
grades = subItems grades = subItems
), ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems ), ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems

View File

@ -1,12 +1,11 @@
package io.github.wulkanowy.ui.modules.grade.summary package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider 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.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber import timber.log.Timber
@ -16,8 +15,6 @@ class GradeSummaryPresenter @Inject constructor(
schedulers: SchedulersProvider, schedulers: SchedulersProvider,
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val gradeSummaryRepository: GradeSummaryRepository,
private val semesterRepository: SemesterRepository,
private val averageProvider: GradeAverageProvider, private val averageProvider: GradeAverageProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<GradeSummaryView>(errorHandler, studentRepository, schedulers) { ) : BasePresenter<GradeSummaryView>(errorHandler, studentRepository, schedulers) {
@ -33,15 +30,8 @@ class GradeSummaryPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade summary data started") Timber.i("Loading grade summary data started")
disposable.add(studentRepository.getCurrentStudent() disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } } .flatMap { averageProvider.getGradesDetailsWithAverage(it, semesterId, forceRefresh) }
.flatMap { (student, semesters) -> .map { createGradeSummaryItems(it) }
gradeSummaryRepository.getGradesSummary(student, semesters.first { it.semesterId == semesterId }, forceRefresh)
.map { it.sortedBy { subject -> subject.subject } }
.flatMap { gradesSummary ->
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
.map { averages -> createGradeSummaryItems(gradesSummary, averages) }
}
}
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doFinally { .doFinally {
@ -112,12 +102,10 @@ class GradeSummaryPresenter @Inject constructor(
disposable.clear() disposable.clear()
} }
private fun createGradeSummaryItems(gradesSummary: List<GradeSummary>, averages: List<Triple<String, Double, String>>): List<GradeSummary> { private fun createGradeSummaryItems(items: List<GradeDetailsWithAverage>): List<GradeSummary> {
return gradesSummary return items.map {
.filter { !checkEmpty(it, averages) } it.summary.copy(average = it.average)
.map { gradeSummary -> }
gradeSummary.copy(average = averages.singleOrNull { gradeSummary.subject == it.first }?.second ?: .0)
}
} }
private fun checkEmpty(gradeSummary: GradeSummary, averages: List<Triple<String, Double, String>>): Boolean { private fun checkEmpty(gradeSummary: GradeSummary, averages: List<Triple<String, Double, String>>): Boolean {

View File

@ -3,17 +3,17 @@ package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.createSemesterEntity import io.github.wulkanowy.data.repositories.createSemesterEntity
import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.doReturn import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.of import org.threeten.bp.LocalDate.of
@ -25,10 +25,10 @@ class GradeAverageProviderTest {
lateinit var preferencesRepository: PreferencesRepository lateinit var preferencesRepository: PreferencesRepository
@Mock @Mock
lateinit var gradeRepository: GradeRepository lateinit var semesterRepository: SemesterRepository
@Mock @Mock
lateinit var gradeSummaryRepository: GradeSummaryRepository lateinit var gradeRepository: GradeRepository
private lateinit var gradeAverageProvider: GradeAverageProvider private lateinit var gradeAverageProvider: GradeAverageProvider
@ -41,165 +41,192 @@ class GradeAverageProviderTest {
) )
private val firstGrades = listOf( private val firstGrades = listOf(
// avg: 3.5
getGrade(22, "Matematyka", 4.0), getGrade(22, "Matematyka", 4.0),
getGrade(22, "Matematyka", 3.0), getGrade(22, "Matematyka", 3.0),
// avg: 3.5
getGrade(22, "Fizyka", 6.0), getGrade(22, "Fizyka", 6.0),
getGrade(22, "Fizyka", 1.0) getGrade(22, "Fizyka", 1.0)
) )
private val secondGrade = listOf( private val firstSummaries = listOf(
getSummary(semesterId = 22, subject = "Matematyka", average = 3.9),
getSummary(semesterId = 22, subject = "Fizyka", average = 3.1)
)
private val secondGrades = listOf(
// avg: 2.5
getGrade(23, "Matematyka", 2.0), getGrade(23, "Matematyka", 2.0),
getGrade(23, "Matematyka", 3.0), getGrade(23, "Matematyka", 3.0),
// avg: 3.0
getGrade(23, "Fizyka", 4.0), getGrade(23, "Fizyka", 4.0),
getGrade(23, "Fizyka", 2.0) getGrade(23, "Fizyka", 2.0)
) )
private val secondSummaries = listOf(
getSummary(semesterId = 23, subject = "Matematyka", average = 2.9),
getSummary(semesterId = 23, subject = "Fizyka", average = 3.4)
)
private val secondGradeWithModifier = listOf( private val secondGradeWithModifier = listOf(
// avg: 3.375
getGrade(24, "Język polski", 3.0, -0.50), getGrade(24, "Język polski", 3.0, -0.50),
getGrade(24, "Język polski", 4.0, 0.25) getGrade(24, "Język polski", 4.0, 0.25)
) )
private val secondSummariesWithModifier = listOf(
getSummary(24, "Język polski", 3.49)
)
@Before @Before
fun initTest() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
gradeAverageProvider = GradeAverageProvider(preferencesRepository, gradeRepository, gradeSummaryRepository)
doReturn(.33).`when`(preferencesRepository).gradeMinusModifier `when`(preferencesRepository.gradeMinusModifier).thenReturn(.33)
doReturn(.33).`when`(preferencesRepository).gradePlusModifier `when`(preferencesRepository.gradePlusModifier).thenReturn(.33)
doReturn(false).`when`(preferencesRepository).gradeAverageForceCalc `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false)
`when`(semesterRepository.getSemesters(student)).thenReturn(Single.just(semesters))
doReturn(Single.just(firstGrades)).`when`(gradeRepository).getGrades(student, semesters[1], true) gradeAverageProvider = GradeAverageProvider(semesterRepository, gradeRepository, preferencesRepository)
doReturn(Single.just(secondGrade)).`when`(gradeRepository).getGrades(student, semesters[2], true)
} }
@Test @Test
fun onlyOneSemesterTest() { fun onlyOneSemesterTest() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true)
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(student, semesters[2], true) `when`(preferencesRepository.gradeAverageMode).thenReturn("only_one_semester")
`when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to secondSummaries))
val averages = gradeAverageProvider.getGradeAverage(student, semesters, semesters[2].semesterId, true) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet()
.blockingGet()
assertEquals(2, averages.size) assertEquals(2, items.size)
assertEquals(2.5, averages.single { it.first == "Matematyka" }.second, .0) assertEquals(2.5, items.single { it.subject == "Matematyka" }.average, .0)
assertEquals(3.0, averages.single { it.first == "Fizyka" }.second, .0) assertEquals(3.0, items.single { it.subject == "Fizyka" }.average, .0)
} }
@Test @Test
fun onlyOneSemester_gradesWithModifiers_default() { fun onlyOneSemester_gradesWithModifiers_default() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true)
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(student, semesters[2], true) `when`(preferencesRepository.gradeAverageMode).thenReturn("only_one_semester")
doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student, semesters[2], true) `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGradeWithModifier to secondSummariesWithModifier))
val averages = gradeAverageProvider.getGradeAverage(student, semesters, semesters[2].semesterId, true) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet()
.blockingGet()
assertEquals(3.5, averages.single { it.first == "Język polski" }.second, .0) assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0)
} }
@Test @Test
fun onlyOneSemester_gradesWithModifiers_api() { fun onlyOneSemester_gradesWithModifiers_api() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode val student = student.copy(loginMode = Sdk.Mode.API.name)
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(student.copy(loginMode = Sdk.Mode.API.name), semesters[2], true)
doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.API.name), semesters[2], true)
val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.API.name), semesters, semesters[2].semesterId, true) `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true)
.blockingGet() `when`(preferencesRepository.gradeAverageMode).thenReturn("only_one_semester")
`when`(semesterRepository.getSemesters(student)).thenReturn(Single.just(semesters))
`when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGradeWithModifier to secondSummariesWithModifier))
assertEquals(3.375, averages.single { it.first == "Język polski" }.second, .0) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet()
assertEquals(3.375, items.single { it.subject == "Język polski" }.average, .0)
} }
@Test @Test
fun onlyOneSemester_gradesWithModifiers_scrapper() { fun onlyOneSemester_gradesWithModifiers_scrapper() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode val student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name)
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(student, semesters[2], true)
doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.SCRAPPER.name), semesters[2], true)
val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.SCRAPPER.name), semesters, semesters[2].semesterId, true) `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true)
.blockingGet() `when`(preferencesRepository.gradeAverageMode).thenReturn("only_one_semester")
`when`(semesterRepository.getSemesters(student)).thenReturn(Single.just(semesters))
`when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGradeWithModifier to secondSummariesWithModifier))
assertEquals(3.5, averages.single { it.first == "Język polski" }.second, .0) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet()
assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0)
} }
@Test @Test
fun onlyOneSemester_gradesWithModifiers_hybrid() { fun onlyOneSemester_gradesWithModifiers_hybrid() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode val student = student.copy(loginMode = Sdk.Mode.HYBRID.name)
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(student.copy(loginMode = Sdk.Mode.HYBRID.name), semesters[2], true)
doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.HYBRID.name), semesters[2], true)
val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.HYBRID.name), semesters, semesters[2].semesterId, true) `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true)
.blockingGet() `when`(preferencesRepository.gradeAverageMode).thenReturn("only_one_semester")
`when`(semesterRepository.getSemesters(student)).thenReturn(Single.just(semesters))
`when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGradeWithModifier to secondSummariesWithModifier))
assertEquals(3.375, averages.single { it.first == "Język polski" }.second, .0) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet()
assertEquals(3.375, items.single { it.subject == "Język polski" }.average, .0)
} }
@Test @Test
fun allYearFirstSemesterTest() { fun allYearFirstSemesterTest() {
doReturn("all_year").`when`(preferencesRepository).gradeAverageMode `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true)
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(student, semesters[1], true) `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year")
`when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to firstSummaries))
val averages = gradeAverageProvider.getGradeAverage(student, semesters, semesters[1].semesterId, true) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[1].semesterId).blockingGet()
.blockingGet()
assertEquals(2, averages.size) assertEquals(2, items.size)
assertEquals(3.5, averages.single { it.first == "Matematyka" }.second, .0) assertEquals(3.5, items.single { it.subject == "Matematyka" }.average, .0)
assertEquals(3.5, averages.single { it.first == "Fizyka" }.second, .0) assertEquals(3.5, items.single { it.subject == "Fizyka" }.average, .0)
} }
@Test @Test
fun allYearSecondSemesterTest() { fun allYearSecondSemesterTest() {
doReturn("all_year").`when`(preferencesRepository).gradeAverageMode `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true)
doReturn(Single.just(firstGrades)).`when`(gradeRepository).getGrades(student, semesters[1], false) `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year")
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(student, semesters[2], true) `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to firstSummaries))
`when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to secondSummaries))
val averages = gradeAverageProvider.getGradeAverage(student, semesters, semesters[2].semesterId, true) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet()
.blockingGet()
assertEquals(2, averages.size) assertEquals(2, items.size)
assertEquals(3.0, averages.single { it.first == "Matematyka" }.second, .0) assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0)
assertEquals(3.25, averages.single { it.first == "Fizyka" }.second, .0) assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0)
} }
@Test(expected = IllegalArgumentException::class) @Test(expected = IllegalArgumentException::class)
fun incorrectAverageModeTest() { fun incorrectAverageModeTest() {
doReturn("test_mode").`when`(preferencesRepository).gradeAverageMode `when`(preferencesRepository.gradeAverageMode).thenReturn("test_mode")
gradeAverageProvider.getGradeAverage(student, semesters, semesters[2].semesterId, true).blockingGet() gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).blockingGet()
} }
@Test @Test
fun onlyOneSemester_averageFromSummary() { fun allYearSemester_averageFromSummary() {
doReturn("all_year").`when`(preferencesRepository).gradeAverageMode `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year")
doReturn(Single.just(firstGrades)).`when`(gradeRepository).getGrades(student, semesters[1], false) `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false)
doReturn(Single.just(listOf( `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to listOf(
getSummary(22, "Matematyka", 3.1), getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.26) getSummary(22, "Fizyka", 3.5)
))).`when`(gradeSummaryRepository).getGradesSummary(student, semesters[2], true) )))
`when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
)))
val averages = gradeAverageProvider.getGradeAverage(student, semesters, semesters[2].semesterId, true) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet()
.blockingGet()
assertEquals(2, averages.size) assertEquals(2, items.size)
assertEquals(3.1, averages.single { it.first == "Matematyka" }.second, .0) assertEquals(3.25, items.single { it.subject == "Matematyka" }.average, .0)
assertEquals(3.26, averages.single { it.first == "Fizyka" }.second, .0) assertEquals(3.75, items.single { it.subject == "Fizyka" }.average, .0)
} }
@Test @Test
fun onlyOneSemester_averageFromSummary_forceCalc() { fun onlyOneSemester_averageFromSummary_forceCalc() {
doReturn(true).`when`(preferencesRepository).gradeAverageForceCalc `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year")
doReturn("all_year").`when`(preferencesRepository).gradeAverageMode `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true)
doReturn(Single.just(firstGrades)).`when`(gradeRepository).getGrades(student, semesters[1], false) `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to firstSummaries))
doReturn(Single.just(listOf( `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to listOf(
getSummary(22, "Matematyka", 3.1), getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 3.26) getSummary(22, "Fizyka", 7.26)
))).`when`(gradeSummaryRepository).getGradesSummary(student, semesters[2], true) )))
val averages = gradeAverageProvider.getGradeAverage(student, semesters, semesters[2].semesterId, true) val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet()
.blockingGet()
assertEquals(2, averages.size) assertEquals(2, items.size)
assertEquals(3.0, averages.single { it.first == "Matematyka" }.second, .0) assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0)
assertEquals(3.25, averages.single { it.first == "Fizyka" }.second, .0) assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0)
} }
private fun getGrade(semesterId: Int, subject: String, value: Double, modifier: Double = 0.0): Grade { private fun getGrade(semesterId: Int, subject: String, value: Double, modifier: Double = 0.0): Grade {
@ -221,12 +248,12 @@ class GradeAverageProviderTest {
) )
} }
private fun getSummary(semesterId: Int, subject: String, value: Double): GradeSummary { private fun getSummary(semesterId: Int, subject: String, average: Double): GradeSummary {
return GradeSummary( return GradeSummary(
studentId = 101, studentId = 101,
semesterId = semesterId, semesterId = semesterId,
subject = subject, subject = subject,
average = value, average = average,
pointsSum = "", pointsSum = "",
proposedPoints = "", proposedPoints = "",
finalPoints = "", finalPoints = "",