From 191b1ad022e19c02bb53231e88e38aba21b63a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 2 Jun 2020 15:51:15 +0200 Subject: [PATCH] Emulate summaries from grade list when summaries are empty (#855) --- app/build.gradle | 2 +- .../ui/modules/grade/GradeAverageProvider.kt | 33 ++- .../grade/summary/GradeSummaryPresenter.kt | 2 +- .../modules/grade/GradeAverageProviderTest.kt | 269 ++++++++++++------ 4 files changed, 215 insertions(+), 91 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2521a10e..f29af84f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -124,7 +124,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.18.1" + implementation "io.github.wulkanowy:sdk:d0081b4" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0" diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt index 954b5756..9582a5db 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -1,5 +1,7 @@ package io.github.wulkanowy.ui.modules.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.Student import io.github.wulkanowy.data.repositories.grade.GradeRepository @@ -58,15 +60,14 @@ class GradeAverageProvider @Inject constructor( val isAnyAverage = summaries.any { it.average != .0 } val allGrades = details.groupBy { it.subject } - summaries.map { summary -> + summaries.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage).map { summary -> val grades = allGrades[summary.subject].orEmpty() GradeDetailsWithAverage( subject = summary.subject, average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) { - grades.map { - if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) - else it - }.calcAverage() + (if (student.loginMode == Sdk.Mode.SCRAPPER.name) + grades.map { it.changeModifier(plusModifier, minusModifier) } + else grades).calcAverage() } else summary.average, points = summary.pointsSum, summary = summary, @@ -75,4 +76,26 @@ class GradeAverageProvider @Inject constructor( } } } + + private fun List.emulateEmptySummaries(student: Student, semester: Semester, grades: List>>, calcAverage: Boolean): List { + if (isNotEmpty() && size == grades.size) return this + + return grades.mapIndexed { i, (subject, details) -> + singleOrNull { it.subject == subject }?.let { return@mapIndexed it } + GradeSummary( + studentId = student.studentId, + semesterId = semester.semesterId, + position = i, + subject = subject, + predictedGrade = "", + finalGrade = "", + proposedPoints = "", + finalPoints = "", + pointsSum = "", + average = if (calcAverage) (if (student.loginMode == Sdk.Mode.SCRAPPER.name) { + details.map { it.changeModifier(plusModifier, minusModifier) } + } else details).calcAverage() else .0 + ) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index a61ad4af..229a0107 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -119,7 +119,7 @@ class GradeSummaryPresenter @Inject constructor( summary.finalGrade.isBlank() && summary.predictedGrade.isBlank() && average == .0 - && points == "-" + && points.isBlank() } } } diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt index fcc30593..ee8634ca 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt @@ -1,11 +1,11 @@ package io.github.wulkanowy.ui.modules.grade +import io.github.wulkanowy.createSemesterEntity import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.createSemesterEntity import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single @@ -84,8 +84,6 @@ class GradeAverageProviderTest { fun setUp() { MockitoAnnotations.initMocks(this) - `when`(preferencesRepository.gradeMinusModifier).thenReturn(.33) - `when`(preferencesRepository.gradePlusModifier).thenReturn(.33) `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false) `when`(semesterRepository.getSemesters(student)).thenReturn(Single.just(semesters)) @@ -93,7 +91,83 @@ class GradeAverageProviderTest { } @Test - fun onlyOneSemesterTest() { + fun `force calc current semester average with default modifiers in scraper mode`() { + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) + `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)) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0) // from details and after set custom plus/minus + } + + @Test + fun `force calc current semester average with custom modifiers in scraper mode`() { + val student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name) + + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) + `when`(preferencesRepository.gradeMinusModifier).thenReturn(.33) + `when`(preferencesRepository.gradePlusModifier).thenReturn(.33) + + `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)) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0) // from details and after set custom plus/minus + } + + @Test + fun `force calc current semester average with custom modifiers in api mode`() { + val student = student.copy(loginMode = Sdk.Mode.API.name) + + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) + `when`(preferencesRepository.gradeMinusModifier).thenReturn(.33) // useless in this mode + `when`(preferencesRepository.gradePlusModifier).thenReturn(.33) + + `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)) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(3.375, items.single { it.subject == "Język polski" }.average, .0) // (from details): 3.375 + } + + @Test + fun `force calc current semester average with custom modifiers in hybrid mode`() { + val student = student.copy(loginMode = Sdk.Mode.HYBRID.name) + + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) + `when`(preferencesRepository.gradeMinusModifier).thenReturn(.33) // useless in this mode + `when`(preferencesRepository.gradePlusModifier).thenReturn(.33) + + `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)) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(3.375, items.single { it.subject == "Język polski" }.average, .0) // (from details): 3.375 + } + + @Test + fun `calc current semester average`() { + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false) + `when`(preferencesRepository.gradeAverageMode).thenReturn("only_one_semester") + `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to secondSummaries)) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(2, items.size) + assertEquals(2.9, items.single { it.subject == "Matematyka" }.average, .0) // from summary: 2,9 + assertEquals(3.4, items.single { it.subject == "Fizyka" }.average, .0) // from details: 3,4 + } + + @Test + fun `force calc current semester average`() { `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) `when`(preferencesRepository.gradeAverageMode).thenReturn("only_one_semester") `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to secondSummaries)) @@ -101,65 +175,12 @@ class GradeAverageProviderTest { val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() assertEquals(2, items.size) - assertEquals(2.5, items.single { it.subject == "Matematyka" }.average, .0) - assertEquals(3.0, items.single { it.subject == "Fizyka" }.average, .0) + assertEquals(2.5, items.single { it.subject == "Matematyka" }.average, .0) // from details: 2,5 + assertEquals(3.0, items.single { it.subject == "Fizyka" }.average, .0) // from details: 3,0 } @Test - fun onlyOneSemester_gradesWithModifiers_default() { - `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) - `when`(preferencesRepository.gradeAverageMode).thenReturn("only_one_semester") - `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGradeWithModifier to secondSummariesWithModifier)) - - val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() - - assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0) - } - - @Test - fun onlyOneSemester_gradesWithModifiers_api() { - val student = student.copy(loginMode = Sdk.Mode.API.name) - - `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) - `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)) - - val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() - - assertEquals(3.375, items.single { it.subject == "Język polski" }.average, .0) - } - - @Test - fun onlyOneSemester_gradesWithModifiers_scrapper() { - val student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name) - - `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) - `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)) - - val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() - - assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0) - } - - @Test - fun onlyOneSemester_gradesWithModifiers_hybrid() { - val student = student.copy(loginMode = Sdk.Mode.HYBRID.name) - - `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) - `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)) - - val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() - - assertEquals(3.375, items.single { it.subject == "Język polski" }.average, .0) - } - - @Test - fun allYearFirstSemesterTest() { + fun `force calc full year average when current is first`() { `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to firstSummaries)) @@ -167,33 +188,19 @@ class GradeAverageProviderTest { val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[1].semesterId).blockingGet() assertEquals(2, items.size) - assertEquals(3.5, items.single { it.subject == "Matematyka" }.average, .0) - assertEquals(3.5, items.single { it.subject == "Fizyka" }.average, .0) - } - - @Test - fun allYearSecondSemesterTest() { - `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) - `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") - `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 items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() - - assertEquals(2, items.size) - assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) - assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) + assertEquals(3.5, items.single { it.subject == "Matematyka" }.average, .0) // (from summary): 3,5 + assertEquals(3.5, items.single { it.subject == "Fizyka" }.average, .0) // (from summary): 3,5 } @Test(expected = IllegalArgumentException::class) - fun incorrectAverageModeTest() { + fun `calc average on invalid mode`() { `when`(preferencesRepository.gradeAverageMode).thenReturn("test_mode") gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).blockingGet() } @Test - fun allYearSemester_averageFromSummary() { + fun `calc full year average`() { `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false) `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to listOf( @@ -208,14 +215,14 @@ class GradeAverageProviderTest { val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() assertEquals(2, items.size) - assertEquals(3.25, items.single { it.subject == "Matematyka" }.average, .0) - assertEquals(3.75, items.single { it.subject == "Fizyka" }.average, .0) + assertEquals(3.25, items.single { it.subject == "Matematyka" }.average, .0) // (from summaries ↑): 3,0 + 3,5 → 3,25 + assertEquals(3.75, items.single { it.subject == "Fizyka" }.average, .0) // (from summaries ↑): 3,5 + 4,0 → 3,75 } @Test - fun onlyOneSemester_averageFromSummary_forceCalc() { - `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") + fun `force calc full year average`() { `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) + `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to firstSummaries)) `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to listOf( getSummary(22, "Matematyka", 1.1), @@ -225,8 +232,102 @@ class GradeAverageProviderTest { val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() assertEquals(2, items.size) - assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) - assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) + assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) // (from details): 3,5 + 2,5 → 3,0 + assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25 + } + + @Test + fun `calc full year average when no summaries`() { + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false) + `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") + + `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to emptyList())) + `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to emptyList())) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(2, items.size) + assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) // (from details): 2,5 + 3,5 → 3,0 + assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25 + } + + @Test + fun `force calc full year average when no summaries`() { + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) + `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") + + `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to emptyList())) + `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to emptyList())) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(2, items.size) + assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) // (from details): 3,5 + 2,5 → 3,0 + assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25 + } + + @Test + fun `calc full year average when missing summaries in both semesters`() { + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false) + `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") + + `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to listOf( + getSummary(22, "Matematyka", 4.0) + ))) + `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to listOf( + getSummary(23, "Matematyka", 3.0) + ))) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(2, items.size) + assertEquals(3.5, items.single { it.subject == "Matematyka" }.average, .0) // (from summaries ↑): 4,0 + 3,0 → 3,5 + assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25 + } + + @Test + fun `calc full year average when missing summary in second semester`() { + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false) + `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") + + `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to firstSummaries)) + `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to secondSummaries.dropLast(1))) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(2, items.size) + assertEquals(3.4, items.single { it.subject == "Matematyka" }.average, .0) // (from summaries): 3,9 + 2,9 → 3,4 + assertEquals(3.05, items.single { it.subject == "Fizyka" }.average, .0) // 3,1 (from summary) + 3,0 (from details) → 3,05 + } + + @Test + fun `calc full year average when missing summary in first semester`() { + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(false) + `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") + + `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to firstSummaries.dropLast(1))) + `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to secondSummaries)) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(2, items.size) + assertEquals(3.4, items.single { it.subject == "Matematyka" }.average, .0) // (from summaries): 3,9 + 2,9 → 3,4 + assertEquals(3.45, items.single { it.subject == "Fizyka" }.average, .0) // 3,5 (from details) + 3,4 (from summary) → 3,45 + } + + @Test + fun `force calc full year average when missing summary in first semester`() { + `when`(preferencesRepository.gradeAverageForceCalc).thenReturn(true) + `when`(preferencesRepository.gradeAverageMode).thenReturn("all_year") + + `when`(gradeRepository.getGrades(student, semesters[1])).thenReturn(Single.just(firstGrades to firstSummaries.dropLast(1))) + `when`(gradeRepository.getGrades(student, semesters[2])).thenReturn(Single.just(secondGrades to secondSummaries)) + + val items = gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId).blockingGet() + + assertEquals(2, items.size) + assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) // (from details): 3,5 + 2,5 → 3,0 + assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25 } private fun getGrade(semesterId: Int, subject: String, value: Double, modifier: Double = 0.0): Grade {