1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2025-01-18 13:26:44 -06:00

Add descriptive grades (#2411)

This commit is contained in:
Rafał Borcz 2024-02-08 09:16:09 +01:00 committed by GitHub
parent bce92b7347
commit 22f72981cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 3248 additions and 224 deletions

File diff suppressed because it is too large Load Diff

View File

@ -253,4 +253,8 @@ internal class DataModule {
@Singleton
@Provides
fun provideAdminMessageDao(database: AppDatabase) = database.adminMessagesDao
@Singleton
@Provides
fun provideGradeDescriptiveDao(database: AppDatabase) = database.gradeDescriptiveDao
}

View File

@ -14,6 +14,7 @@ import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.dao.ConferenceDao
import io.github.wulkanowy.data.db.dao.ExamDao
import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
@ -44,6 +45,7 @@ import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
@ -154,7 +156,8 @@ import javax.inject.Singleton
TimetableHeader::class,
SchoolAnnouncement::class,
Notification::class,
AdminMessage::class
AdminMessage::class,
GradeDescriptive::class,
],
autoMigrations = [
AutoMigration(from = 44, to = 45),
@ -165,6 +168,7 @@ import javax.inject.Singleton
AutoMigration(from = 55, to = 56),
AutoMigration(from = 56, to = 57, spec = Migration57::class),
AutoMigration(from = 57, to = 58, spec = Migration58::class),
AutoMigration(from = 58, to = 59),
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@ -173,7 +177,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 58
const val VERSION_SCHEMA = 59
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@ -298,4 +302,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val notificationDao: NotificationDao
abstract val adminMessagesDao: AdminMessageDao
abstract val gradeDescriptiveDao: GradeDescriptiveDao
}

View File

@ -0,0 +1,15 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeDescriptive
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@Dao
interface GradeDescriptiveDao : BaseDao<GradeDescriptive> {
@Query("SELECT * FROM GradesDescriptive WHERE semester_id = :semesterId AND student_id = :studentId")
fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradeDescriptive>>
}

View File

@ -0,0 +1,27 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@Entity(tableName = "GradesDescriptive")
data class GradeDescriptive(
@ColumnInfo(name = "semester_id")
val semesterId: Int,
@ColumnInfo(name = "student_id")
val studentId: Int,
val subject: String,
val description: String,
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
@ColumnInfo(name = "is_notified")
var isNotified: Boolean = true
}

View File

@ -1,10 +1,12 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.pojo.GradeSummary as SdkGradeSummary
import io.github.wulkanowy.sdk.pojo.Grade as SdkGrade
import io.github.wulkanowy.sdk.pojo.GradeDescriptive as SdkGradeDescriptive
import io.github.wulkanowy.sdk.pojo.GradeSummary as SdkGradeSummary
fun List<SdkGrade>.mapToEntities(semester: Semester) = map {
Grade(
@ -40,3 +42,15 @@ fun List<SdkGradeSummary>.mapToEntities(semester: Semester) = map {
average = it.average
)
}
@JvmName("mapGradeDescriptiveToEntities")
fun List<SdkGradeDescriptive>.mapToEntities(semester: Semester) = map {
GradeDescriptive(
semesterId = semester.semesterId,
studentId = semester.studentId,
subject = it.subject,
description = it.description
)
}

View File

@ -1,15 +1,22 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeDescriptive
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.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.switchSemester
import io.github.wulkanowy.utils.toLocalDate
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
@ -22,14 +29,13 @@ import javax.inject.Singleton
class GradeRepository @Inject constructor(
private val gradeDb: GradeDao,
private val gradeSummaryDb: GradeSummaryDao,
private val gradeDescriptiveDb: GradeDescriptiveDao,
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val saveFetchResultMutex = Mutex()
private val cacheKey = "grade"
fun getGrades(
student: Student,
semester: Semester,
@ -41,30 +47,52 @@ class GradeRepository @Inject constructor(
//When details is empty and summary is not, app will not use summary cache - edge case
it.first.isEmpty()
},
shouldFetch = { (details, summaries) ->
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
details.isEmpty() || summaries.isEmpty() || forceRefresh || isExpired
shouldFetch = { (details, summaries, descriptive) ->
val isExpired =
refreshHelper.shouldBeRefreshed(getRefreshKey(GRADE_CACHE_KEY, semester))
details.isEmpty() || (summaries.isEmpty() && descriptive.isEmpty()) || forceRefresh || isExpired
},
query = {
val detailsFlow = gradeDb.loadAll(semester.semesterId, semester.studentId)
val summaryFlow = gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
detailsFlow.combine(summaryFlow) { details, summaries -> details to summaries }
val descriptiveFlow =
gradeDescriptiveDb.loadAll(semester.semesterId, semester.studentId)
combine(detailsFlow, summaryFlow, descriptiveFlow) { details, summaries, descriptive ->
Triple(details, summaries, descriptive)
}
},
fetch = {
val (details, summary) = sdk.init(student)
val (details, summary, descriptive) = sdk.init(student)
.switchSemester(semester)
.getGrades(semester.semesterId)
details.mapToEntities(semester) to summary.mapToEntities(semester)
Triple(
details.mapToEntities(semester),
summary.mapToEntities(semester),
descriptive.mapToEntities(semester)
)
},
saveFetchResult = { (oldDetails, oldSummary), (newDetails, newSummary) ->
saveFetchResult = { (oldDetails, oldSummary, oldDescriptive), (newDetails, newSummary, newDescriptive) ->
refreshGradeDetails(student, oldDetails, newDetails, notify)
refreshGradeSummaries(oldSummary, newSummary, notify)
refreshGradeDescriptions(oldDescriptive, newDescriptive, notify)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(GRADE_CACHE_KEY, semester))
}
)
private suspend fun refreshGradeDescriptions(
old: List<GradeDescriptive>,
new: List<GradeDescriptive>,
notify: Boolean
) {
gradeDescriptiveDb.deleteAll(old uniqueSubtract new)
gradeDescriptiveDb.insertAll((new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
})
}
private suspend fun refreshGradeDetails(
student: Student,
oldGrades: List<Grade>,
@ -132,6 +160,10 @@ class GradeRepository @Inject constructor(
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
}
fun getGradesDescriptiveFromDatabase(semester: Semester): Flow<List<GradeDescriptive>> {
return gradeDescriptiveDb.loadAll(semester.semesterId, semester.studentId)
}
suspend fun updateGrade(grade: Grade) {
return gradeDb.updateAll(listOf(grade))
}
@ -143,4 +175,13 @@ class GradeRepository @Inject constructor(
suspend fun updateGradesSummary(gradesSummary: List<GradeSummary>) {
return gradeSummaryDb.updateAll(gradesSummary)
}
suspend fun updateGradesDescriptive(gradesDescriptive: List<GradeDescriptive>) {
return gradeDescriptiveDb.updateAll(gradesDescriptive)
}
private companion object {
private const val GRADE_CACHE_KEY = "grade"
}
}

View File

@ -4,12 +4,12 @@ import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.GroupNotificationData
import io.github.wulkanowy.data.pojos.NotificationData
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural
import javax.inject.Inject
@ -88,4 +88,28 @@ class NewGradeNotification @Inject constructor(
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
}
suspend fun notifyDescriptive(items: List<GradeDescriptive>, student: Student) {
val notificationDataList = items.map {
NotificationData(
title = context.getPlural(R.plurals.grade_new_items_descriptive, 1),
content = "${it.subject}: ${it.description}",
destination = Destination.Grade,
)
}
val groupNotificationData = GroupNotificationData(
notificationDataList = notificationDataList,
title = context.getPlural(R.plurals.grade_new_items_descriptive, items.size),
content = context.getPlural(
R.plurals.grade_notify_new_items_descriptive,
items.size,
items.size
),
destination = Destination.Grade,
type = NotificationType.NEW_GRADE_DESCRIPTIVE
)
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
}
}

View File

@ -37,6 +37,10 @@ enum class NotificationType(
channel = NewGradesChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_grade,
),
NEW_GRADE_DESCRIPTIVE(
channel = NewGradesChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_grade,
),
NEW_HOMEWORK(
channel = NewHomeworkChannel.CHANNEL_ID,
icon = R.drawable.ic_more_homework,

View File

@ -45,5 +45,15 @@ class GradeWork @Inject constructor(
grade.isFinalGradeNotified = true
})
}
gradeRepository.getGradesDescriptiveFromDatabase(semester).first()
.filter { !it.isNotified }
.let {
if (it.isNotEmpty()) newGradeNotification.notifyDescriptive(it, student)
gradeRepository.updateGradesDescriptive(it.onEach { grade ->
grade.isNotified = true
})
}
}
}

View File

@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugAttendanceItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugConferenceItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugExamItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeDescriptiveItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeDetailsItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeSummaryItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugHomeworkItems
@ -55,6 +56,14 @@ class NotificationDebugPresenter @Inject constructor(
NotificationDebugItem(R.string.grade_summary_final_grade) { n ->
withStudent { newGradeNotification.notifyFinal(debugGradeSummaryItems.take(n), it) }
},
NotificationDebugItem(R.string.grade_summary_descriptive) { n ->
withStudent {
newGradeNotification.notifyDescriptive(
debugGradeDescriptiveItems.take(n),
it
)
}
},
NotificationDebugItem(R.string.homework_title) { n ->
withStudent { newHomeworkNotification.notify(debugHomeworkItems.take(n), it) }
},

View File

@ -0,0 +1,48 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.GradeDescriptive
val debugGradeDescriptiveItems = listOf(
generateGradeDescriptive(
"Matematyka",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
),
generateGradeDescriptive("Fizyka", "Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
generateGradeDescriptive(
"Geografia",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
),
generateGradeDescriptive(
"Sieci komputerowe",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
),
generateGradeDescriptive(
"Systemy operacyjne",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
),
generateGradeDescriptive(
"Język polski",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
),
generateGradeDescriptive(
"Język angielski",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
),
generateGradeDescriptive("Religia", "Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
generateGradeDescriptive(
"Język niemiecki",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
),
generateGradeDescriptive(
"Wychowanie fizyczne",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
),
)
private fun generateGradeDescriptive(subject: String, description: String) =
GradeDescriptive(
semesterId = 0,
studentId = 0,
subject = subject,
description = description
)

View File

@ -1,15 +1,23 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeDescriptive
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.errorOrNull
import io.github.wulkanowy.data.flatResourceFlow
import io.github.wulkanowy.data.mapData
import io.github.wulkanowy.data.mapResourceData
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ALL_YEAR
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 kotlinx.coroutines.ExperimentalCoroutinesApi
@ -62,6 +70,7 @@ class GradeAverageProvider @Inject constructor(
forceRefresh = forceRefresh,
params = params,
)
BOTH_SEMESTERS -> calculateCombinedAverage(
student = student,
semesters = semesters,
@ -69,6 +78,7 @@ class GradeAverageProvider @Inject constructor(
forceRefresh = forceRefresh,
config = params,
)
ALL_YEAR -> calculateCombinedAverage(
student = student,
semesters = semesters,
@ -189,36 +199,73 @@ class GradeAverageProvider @Inject constructor(
): Flow<Resource<List<GradeSubject>>> {
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
.mapResourceData { res ->
val (details, summaries) = res
val (details, summaries, descriptives) = res
val isAnyAverage = summaries.any { it.average != .0 }
val allGrades = details.groupBy { it.subject }
val descriptiveGradesBySubject = descriptives.associateBy { it.subject }
val items = summaries.emulateEmptySummaries(
student = student,
semester = semester,
grades = allGrades.toList(),
calcAverage = isAnyAverage,
params = params,
).map { summary ->
val grades = allGrades[summary.subject].orEmpty()
GradeSubject(
subject = summary.subject,
average = if (!isAnyAverage || params.forceAverageCalc) {
grades.updateModifiers(student, params)
.calcAverage(params.isOptionalArithmeticAverage)
} else summary.average,
points = summary.pointsSum,
summary = summary,
grades = grades,
isVulcanAverage = isAnyAverage
val items = summaries
.createEmptySummariesByGradesIfNeeded(
student = student,
semester = semester,
grades = allGrades.toList(),
calcAverage = isAnyAverage,
params = params,
)
}
.createEmptySummariesByDescriptiveGradesIfNeeded(
student = student,
semester = semester,
descriptives = descriptives,
)
.map { summary ->
val grades = allGrades[summary.subject].orEmpty()
val descriptiveGrade = descriptiveGradesBySubject[summary.subject]
GradeSubject(
subject = summary.subject,
average = if (!isAnyAverage || params.forceAverageCalc) {
grades.updateModifiers(student, params)
.calcAverage(params.isOptionalArithmeticAverage)
} else summary.average,
points = summary.pointsSum,
summary = summary,
grades = grades,
descriptive = descriptiveGrade,
isVulcanAverage = isAnyAverage
)
}
items
}
}
private fun List<GradeSummary>.emulateEmptySummaries(
private fun List<GradeSummary>.createEmptySummariesByDescriptiveGradesIfNeeded(
student: Student,
semester: Semester,
descriptives: List<GradeDescriptive>
): List<GradeSummary> {
val summarySubjects = this.map { it.subject }
val gradeSummaryToAdd = descriptives.mapNotNull { gradeDescriptive ->
if (gradeDescriptive.subject in summarySubjects) return@mapNotNull null
GradeSummary(
studentId = student.studentId,
semesterId = semester.semesterId,
position = 0,
subject = gradeDescriptive.subject,
predictedGrade = "",
finalGrade = "",
proposedPoints = "",
finalPoints = "",
pointsSum = "",
average = .0
)
}
return this + gradeSummaryToAdd
}
private fun List<GradeSummary>.createEmptySummariesByGradesIfNeeded(
student: Student,
semester: Semester,
grades: List<Pair<String, List<Grade>>>,

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
data class GradeSubject(
@ -8,6 +9,7 @@ data class GradeSubject(
val average: Double,
val points: String,
val summary: GradeSummary,
val descriptive: GradeDescriptive?,
val grades: List<Grade>,
val isVulcanAverage: Boolean
)

View File

@ -1,13 +1,22 @@
package io.github.wulkanowy.ui.modules.grade.details
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.enums.GradeExpandMode
import io.github.wulkanowy.data.enums.GradeSortingMode.*
import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC
import io.github.wulkanowy.data.enums.GradeSortingMode.AVERAGE
import io.github.wulkanowy.data.enums.GradeSortingMode.DATE
import io.github.wulkanowy.data.flatResourceFlow
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceData
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceIntermediate
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
@ -207,20 +216,20 @@ class GradeDetailsPresenter @Inject constructor(
AVERAGE -> gradeSubjects.sortedByDescending { it.average }
}
}
.map { (subject, average, points, _, grades) ->
val subItems = grades
.map { gradeSubject ->
val subItems = gradeSubject.grades
.sortedByDescending { it.date }
.map { GradeDetailsItem(it, ViewType.ITEM) }
val gradeDetailsItems = listOf(
GradeDetailsItem(
GradeDetailsHeader(
subject = subject,
average = average,
pointsSum = points,
subject = gradeSubject.subject,
average = gradeSubject.average,
pointsSum = gradeSubject.points,
grades = subItems
).apply {
newGrades = grades.filter { grade -> !grade.isRead }.size
newGrades = gradeSubject.grades.filter { grade -> !grade.isRead }.size
}, ViewType.HEADER
)
)

View File

@ -2,16 +2,16 @@ package io.github.wulkanowy.ui.modules.grade.summary
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.ItemGradeSummaryBinding
import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding
import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid
import io.github.wulkanowy.utils.calcFinalAverage
import io.github.wulkanowy.utils.ifNullOrBlank
import java.util.Locale
import javax.inject.Inject
@ -24,7 +24,7 @@ class GradeSummaryAdapter @Inject constructor(
ITEM(2)
}
var items = emptyList<GradeSummary>()
var items = emptyList<GradeSummaryItem>()
var onCalculatedHelpClickListener: () -> Unit = {}
@ -44,9 +44,11 @@ class GradeSummaryAdapter @Inject constructor(
ViewType.HEADER.id -> HeaderViewHolder(
ScrollableHeaderGradeSummaryBinding.inflate(inflater, parent, false)
)
ViewType.ITEM.id -> ItemViewHolder(
ItemGradeSummaryBinding.inflate(inflater, parent, false)
)
else -> throw IllegalStateException()
}
}
@ -60,19 +62,23 @@ class GradeSummaryAdapter @Inject constructor(
private fun bindHeaderViewHolder(binding: ScrollableHeaderGradeSummaryBinding) {
if (items.isEmpty()) return
val gradeSummaries = items
.filter { it.gradeDescriptive == null }
.map { it.gradeSummary }
val context = binding.root.context
val finalItemsCount = items.count { isGradeValid(it.finalGrade) }
val calculatedItemsCount = items.count { value -> value.average != 0.0 }
val allItemsCount = items.count { !it.subject.equals("zachowanie", true) }
val finalAverage = items.calcFinalAverage(
val finalItemsCount = gradeSummaries.count { isGradeValid(it.finalGrade) }
val calculatedItemsCount = gradeSummaries.count { value -> value.average != 0.0 }
val allItemsCount = gradeSummaries.count { !it.subject.equals("zachowanie", true) }
val finalAverage = gradeSummaries.calcFinalAverage(
preferencesRepository.gradePlusModifier,
preferencesRepository.gradeMinusModifier
)
val calculatedAverage = items.filter { value -> value.average != 0.0 }
val calculatedAverage = gradeSummaries.filter { value -> value.average != 0.0 }
.map { values -> values.average }
.reversed() // fix average precision
.average()
.let { if (it.isNaN()) 0.0 else it }
with(binding) {
gradeSummaryScrollableHeaderFinal.text = formatAverage(finalAverage)
@ -95,16 +101,28 @@ class GradeSummaryAdapter @Inject constructor(
}
@SuppressLint("SetTextI18n")
private fun bindItemViewHolder(binding: ItemGradeSummaryBinding, item: GradeSummary) {
with(binding) {
gradeSummaryItemTitle.text = item.subject
gradeSummaryItemPoints.text = item.pointsSum
gradeSummaryItemAverage.text = formatAverage(item.average, "")
gradeSummaryItemPredicted.text = "${item.predictedGrade} ${item.proposedPoints}".trim()
gradeSummaryItemFinal.text = "${item.finalGrade} ${item.finalPoints}".trim()
private fun bindItemViewHolder(binding: ItemGradeSummaryBinding, item: GradeSummaryItem) {
val (gradeSummary, gradeDescriptive) = item
gradeSummaryItemPointsContainer.visibility =
if (item.pointsSum.isBlank()) View.GONE else View.VISIBLE
with(binding) {
gradeSummaryItemTitle.text = gradeSummary.subject
gradeSummaryItemPoints.text = gradeSummary.pointsSum
gradeSummaryItemAverage.text = formatAverage(gradeSummary.average, "")
gradeSummaryItemPredicted.text =
"${gradeSummary.predictedGrade} ${gradeSummary.proposedPoints}".trim()
gradeSummaryItemFinal.text =
"${gradeSummary.finalGrade} ${gradeSummary.finalPoints}".trim()
gradeSummaryItemDescriptive.text = gradeDescriptive?.description.ifNullOrBlank {
root.context.getString(R.string.all_no_data)
}
gradeSummaryItemFinalDivider.isVisible = gradeDescriptive == null
gradeSummaryItemPredictedDivider.isVisible = gradeDescriptive == null
gradeSummaryItemPointsDivider.isVisible = gradeDescriptive == null
gradeSummaryItemPredictedContainer.isVisible = gradeDescriptive == null
gradeSummaryItemFinalContainer.isVisible = gradeDescriptive == null
gradeSummaryItemDescriptiveContainer.isVisible = gradeDescriptive != null
gradeSummaryItemPointsContainer.isVisible = gradeSummary.pointsSum.isNotBlank()
}
}

View File

@ -5,12 +5,10 @@ import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.databinding.FragmentGradeSummaryBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
@ -72,7 +70,7 @@ class GradeSummaryFragment :
}
}
override fun updateData(data: List<GradeSummary>) {
override fun updateData(data: List<GradeSummaryItem>) {
with(gradeSummaryAdapter) {
items = data
notifyDataSetChanged()

View File

@ -0,0 +1,9 @@
package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
data class GradeSummaryItem(
val gradeSummary: GradeSummary,
val gradeDescriptive: GradeDescriptive?
)

View File

@ -1,9 +1,16 @@
package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.enums.GradeSortingMode
import io.github.wulkanowy.data.enums.GradeSortingMode.*
import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC
import io.github.wulkanowy.data.enums.GradeSortingMode.AVERAGE
import io.github.wulkanowy.data.enums.GradeSortingMode.DATE
import io.github.wulkanowy.data.flatResourceFlow
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.mapResourceData
import io.github.wulkanowy.data.onResourceData
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceIntermediate
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@ -128,7 +135,7 @@ class GradeSummaryPresenter @Inject constructor(
view?.showFinalAverageHelpDialog()
}
private fun createGradeSummaryItems(items: List<GradeSubject>): List<GradeSummary> {
private fun createGradeSummaryItems(items: List<GradeSubject>): List<GradeSummaryItem> {
return items
.filter { !checkEmpty(it) }
.let { gradeSubjects ->
@ -136,21 +143,32 @@ class GradeSummaryPresenter @Inject constructor(
DATE -> gradeSubjects.sortedByDescending { gradeDetailsWithAverage ->
gradeDetailsWithAverage.grades.maxByOrNull { it.date }?.date
}
ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage ->
gradeDetailsWithAverage.subject.lowercase()
}
AVERAGE -> gradeSubjects.sortedByDescending { it.average }
}
}
.map { it.summary.copy(average = it.average) }
.map {
val gradeSummary = it.summary.copy(average = it.average)
val descriptive = it.descriptive
GradeSummaryItem(
gradeSummary = gradeSummary,
gradeDescriptive = descriptive,
)
}
}
private fun checkEmpty(gradeSummary: GradeSubject): Boolean {
return gradeSummary.run {
summary.finalGrade.isBlank()
&& summary.predictedGrade.isBlank()
&& average == .0
&& points.isBlank()
&& summary.predictedGrade.isBlank()
&& average == .0
&& points.isBlank()
&& descriptive == null
}
}
}

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.ui.base.BaseView
interface GradeSummaryView : BaseView {
@ -13,7 +12,7 @@ interface GradeSummaryView : BaseView {
fun initView()
fun updateData(data: List<GradeSummary>)
fun updateData(data: List<GradeSummaryItem>)
fun resetView()

View File

@ -64,10 +64,12 @@
<View
android:layout_width="match_parent"
android:background="@drawable/ic_all_divider"
android:layout_height="1dp" />
android:layout_height="1dp"
android:id="@+id/gradeSummaryItemPointsDivider"
android:background="@drawable/ic_all_divider" />
<LinearLayout
android:id="@+id/gradeSummaryItemPredictedContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="35dp"
@ -96,10 +98,12 @@
<View
android:layout_width="match_parent"
android:background="@drawable/ic_all_divider"
android:layout_height="1dp" />
android:id="@+id/gradeSummaryItemPredictedDivider"
android:layout_height="1dp"
android:background="@drawable/ic_all_divider" />
<LinearLayout
android:id="@+id/gradeSummaryItemFinalContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="35dp"
@ -128,6 +132,36 @@
<View
android:layout_width="match_parent"
android:background="@drawable/ic_all_divider"
android:layout_height="1dp" />
android:layout_height="1dp"
android:id="@+id/gradeSummaryItemFinalDivider"
android:background="@drawable/ic_all_divider" />
<LinearLayout
android:id="@+id/gradeSummaryItemDescriptiveContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="35dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="7dp"
android:fontFamily="sans-serif-medium"
android:text="@string/grade_summary_descriptive"
android:textSize="14sp" />
<TextView
android:id="@+id/gradeSummaryItemDescriptive"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="7dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="7dp"
android:textSize="14sp"
tools:maxLines="4"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>
</LinearLayout>

View File

@ -130,6 +130,7 @@
<string name="grade_summary_points">Total points</string>
<string name="grade_summary_final_grade">Final grade</string>
<string name="grade_summary_predicted_grade">Predicted grade</string>
<string name="grade_summary_descriptive">Descriptive grade</string>
<string name="grade_summary_calculated_average">Calculated average</string>
<string name="grade_summary_calculated_average_help_dialog_title">How does Calculated Average work?</string>
<string name="grade_summary_calculated_average_help_dialog_message">The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\n<b>Average of grades only from selected semester</b>:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\n<b>Average of averages from both semesters</b>:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\n<b>Average of grades from the whole year:</b>\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages</string>
@ -165,6 +166,10 @@
<item quantity="one">New final grade</item>
<item quantity="other">New final grades</item>
</plurals>
<plurals name="grade_new_items_descriptive">
<item quantity="one">New descriptive grade</item>
<item quantity="other">New descriptive grades</item>
</plurals>
<plurals name="grade_notify_new_items">
<item quantity="one">You received %1$d grade</item>
<item quantity="other">You received %1$d grades</item>
@ -177,6 +182,10 @@
<item quantity="one">You received %1$d final grade</item>
<item quantity="other">You received %1$d final grades</item>
</plurals>
<plurals name="grade_notify_new_items_descriptive">
<item quantity="one">You received %1$d descriptive grade</item>
<item quantity="other">You received %1$d descriptive grades</item>
</plurals>
<!--Timetable-->

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
@ -42,6 +43,9 @@ class GradeRepositoryTest {
@MockK
private lateinit var gradeSummaryDb: GradeSummaryDao
@MockK
private lateinit var gradeDescriptiveDb: GradeDescriptiveDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
@ -56,7 +60,8 @@ class GradeRepositoryTest {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
gradeRepository = GradeRepository(gradeDb, gradeSummaryDb, sdk, refreshHelper)
gradeRepository =
GradeRepository(gradeDb, gradeSummaryDb, gradeDescriptiveDb, sdk, refreshHelper)
coEvery { gradeDb.deleteAll(any()) } just Runs
coEvery { gradeDb.insertAll(any()) } returns listOf()
@ -68,6 +73,13 @@ class GradeRepositoryTest {
)
coEvery { gradeSummaryDb.deleteAll(any()) } just Runs
coEvery { gradeSummaryDb.insertAll(any()) } returns listOf()
coEvery { gradeDescriptiveDb.loadAll(any(), any()) } returnsMany listOf(
flowOf(listOf()),
)
coEvery { gradeDescriptiveDb.deleteAll(any()) } just Runs
coEvery { gradeDescriptiveDb.insertAll(any()) } returns listOf()
}
@Test

View File

@ -1,12 +1,16 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.dataOrNull
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.errorOrNull
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.Status
@ -158,7 +162,9 @@ class GradeAverageProviderTest {
semesters[2],
true
)
} returns resourceFlow { noWeightGrades to noWeightGradesSummary }
} returns resourceFlow {
Triple(noWeightGrades, noWeightGradesSummary, emptyList())
}
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -186,7 +192,9 @@ class GradeAverageProviderTest {
semesters[2],
true
)
} returns resourceFlow { noWeightGrades to noWeightGradesArithmeticSummary }
} returns resourceFlow {
Triple(noWeightGrades, noWeightGradesArithmeticSummary, emptyList())
}
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -211,8 +219,24 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flow {
emit(Resource.Loading())
emit(Resource.Intermediate(secondGradeWithModifier to secondSummariesWithModifier))
emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
emit(
Resource.Intermediate(
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
)
)
emit(
Resource.Success(
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
)
)
}
val items = runBlocking {
@ -253,11 +277,27 @@ class GradeAverageProviderTest {
coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns flow {
emit(Resource.Loading())
delay(1000)
emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
emit(
Resource.Success(
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
)
)
}
coEvery { gradeRepository.getGrades(student, semesters[1], false) } returns flow {
emit(Resource.Loading())
emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
emit(
Resource.Success(
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
)
)
}
val items = runBlocking {
@ -296,7 +336,13 @@ class GradeAverageProviderTest {
semesters[1],
false
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
} returns resourceFlow {
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
}
coEvery {
gradeRepository.getGrades(
student,
@ -304,8 +350,10 @@ class GradeAverageProviderTest {
false
)
} returns resourceFlow {
listOf(getGrade(semesters[2].semesterId, "Język polski", .0, .0, .0)) to listOf(
getSummary(semesters[2].semesterId, "Język polski", 2.5)
Triple(
listOf(getGrade(semesters[2].semesterId, "Język polski", .0, .0, .0)),
listOf(getSummary(semesters[2].semesterId, "Język polski", 2.5)),
emptyList()
)
}
@ -332,7 +380,13 @@ class GradeAverageProviderTest {
semesters[1],
false
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
} returns resourceFlow {
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
}
coEvery {
gradeRepository.getGrades(
student,
@ -340,12 +394,14 @@ class GradeAverageProviderTest {
false
)
} returns resourceFlow {
emptyList<Grade>() to listOf(
getSummary(
24,
"Język polski",
.0
)
Triple(
emptyList(), listOf(
getSummary(
24,
"Język polski",
.0
)
), emptyList()
)
}
@ -372,14 +428,22 @@ class GradeAverageProviderTest {
semesters[1],
true
)
} returns resourceFlow { emptyList<Grade>() to emptyList() }
} returns resourceFlow {
Triple(
emptyList(),
emptyList(),
emptyList()
)
}
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
} returns resourceFlow { emptyList<Grade>() to emptyList() }
} returns resourceFlow {
Triple(emptyList(), emptyList(), emptyList())
}
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -404,7 +468,13 @@ class GradeAverageProviderTest {
semesters[2],
true
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
} returns resourceFlow {
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
}
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -438,7 +508,13 @@ class GradeAverageProviderTest {
semesters[2],
true
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
} returns resourceFlow {
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
}
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -472,7 +548,13 @@ class GradeAverageProviderTest {
semesters[2],
true
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
} returns resourceFlow {
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
}
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -506,7 +588,13 @@ class GradeAverageProviderTest {
semesters[2],
true
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
} returns resourceFlow {
Triple(
secondGradeWithModifier,
secondSummariesWithModifier,
emptyList()
)
}
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -534,7 +622,7 @@ class GradeAverageProviderTest {
semesters[2],
true
)
} returns resourceFlow { secondGrades to secondSummaries }
} returns resourceFlow { Triple(secondGrades, secondSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -564,7 +652,7 @@ class GradeAverageProviderTest {
semesters[2],
true
)
} returns resourceFlow { secondGrades to secondSummaries }
} returns resourceFlow { Triple(secondGrades, secondSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -594,7 +682,7 @@ class GradeAverageProviderTest {
semesters[1],
true
)
} returns resourceFlow { firstGrades to firstSummaries }
} returns resourceFlow { Triple(firstGrades, firstSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -625,8 +713,8 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
emit(Resource.Loading())
emit(Resource.Intermediate(firstGrades to firstSummaries))
emit(Resource.Success(firstGrades to firstSummaries))
emit(Resource.Intermediate(Triple(firstGrades, firstSummaries, emptyList())))
emit(Resource.Success(Triple(firstGrades, firstSummaries, emptyList())))
}
val items = runBlocking {
@ -675,9 +763,11 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
firstGrades to listOf(
getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.5)
Triple(
firstGrades, listOf(
getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.5)
), emptyList()
)
}
coEvery {
@ -687,9 +777,13 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
secondGrades to listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
Triple(
secondGrades,
listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
),
emptyList()
)
}
@ -723,17 +817,21 @@ class GradeAverageProviderTest {
emit(Resource.Loading())
emit(
Resource.Intermediate(
firstGrades to listOf(
getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.5)
Triple(
firstGrades, listOf(
getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.5)
), emptyList()
)
)
)
emit(
Resource.Success(
firstGrades to listOf(
getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.5)
Triple(
firstGrades, listOf(
getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.5)
), emptyList()
)
)
)
@ -742,17 +840,21 @@ class GradeAverageProviderTest {
emit(Resource.Loading())
emit(
Resource.Intermediate(
secondGrades to listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
Triple(
secondGrades, listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
), emptyList()
)
)
)
emit(
Resource.Success(
secondGrades to listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
Triple(
secondGrades, listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
), emptyList()
)
)
)
@ -803,7 +905,7 @@ class GradeAverageProviderTest {
semesters[1],
true
)
} returns resourceFlow { firstGrades to firstSummaries }
} returns resourceFlow { Triple(firstGrades, firstSummaries, emptyList()) }
coEvery {
gradeRepository.getGrades(
student,
@ -811,9 +913,11 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
secondGrades to listOf(
getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 7.26)
Triple(
secondGrades, listOf(
getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 7.26)
), emptyList()
)
}
@ -850,9 +954,11 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
firstGrades to listOf(
getSummary(22, "Matematyka", .0),
getSummary(22, "Fizyka", .0)
Triple(
firstGrades, listOf(
getSummary(22, "Matematyka", .0),
getSummary(22, "Fizyka", .0)
), emptyList()
)
}
coEvery {
@ -862,9 +968,11 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
secondGrades to listOf(
getSummary(22, "Matematyka", .0),
getSummary(22, "Fizyka", .0)
Triple(
secondGrades, listOf(
getSummary(22, "Matematyka", .0),
getSummary(22, "Fizyka", .0)
), emptyList()
)
}
@ -889,24 +997,28 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
emit(Resource.Loading())
emit(Resource.Intermediate(firstGrades to firstSummaries))
emit(Resource.Success(firstGrades to firstSummaries))
emit(Resource.Intermediate(Triple(firstGrades, firstSummaries, emptyList())))
emit(Resource.Success(Triple(firstGrades, firstSummaries, emptyList())))
}
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flow {
emit(Resource.Loading())
emit(
Resource.Intermediate(
secondGrades to listOf(
getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 7.26)
Triple(
secondGrades, listOf(
getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 7.26)
), emptyList()
)
)
)
emit(
Resource.Success(
secondGrades to listOf(
getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 7.26)
Triple(
secondGrades, listOf(
getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 7.26)
), emptyList()
)
)
)
@ -958,14 +1070,14 @@ class GradeAverageProviderTest {
semesters[1],
true
)
} returns resourceFlow { firstGrades to emptyList() }
} returns resourceFlow { Triple(firstGrades, emptyList(), emptyList()) }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
} returns resourceFlow { secondGrades to emptyList() }
} returns resourceFlow { Triple(secondGrades, emptyList(), emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -1000,14 +1112,14 @@ class GradeAverageProviderTest {
semesters[1],
true
)
} returns resourceFlow { firstGrades to emptyList() }
} returns resourceFlow { Triple(firstGrades, emptyList(), emptyList()) }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
} returns resourceFlow { secondGrades to emptyList() }
} returns resourceFlow { Triple(secondGrades, emptyList(), emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -1043,8 +1155,10 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
firstGrades to listOf(
getSummary(22, "Matematyka", 4.0)
Triple(
firstGrades, listOf(
getSummary(22, "Matematyka", 4.0)
), emptyList()
)
}
coEvery {
@ -1054,8 +1168,10 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
secondGrades to listOf(
getSummary(23, "Matematyka", 3.0)
Triple(
secondGrades, listOf(
getSummary(23, "Matematyka", 3.0)
), emptyList()
)
}
@ -1092,14 +1208,20 @@ class GradeAverageProviderTest {
semesters[1],
true
)
} returns resourceFlow { firstGrades to firstSummaries }
} returns resourceFlow { Triple(firstGrades, firstSummaries, emptyList()) }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
} returns resourceFlow { secondGrades to secondSummaries.dropLast(1) }
} returns resourceFlow {
Triple(
secondGrades,
secondSummaries.dropLast(1),
emptyList()
)
}
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -1134,14 +1256,20 @@ class GradeAverageProviderTest {
semesters[1],
true
)
} returns resourceFlow { firstGrades to firstSummaries.dropLast(1) }
} returns resourceFlow {
Triple(
firstGrades,
firstSummaries.dropLast(1),
emptyList()
)
}
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
} returns resourceFlow { secondGrades to secondSummaries }
} returns resourceFlow { Triple(secondGrades, secondSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -1176,14 +1304,20 @@ class GradeAverageProviderTest {
semesters[1],
true
)
} returns resourceFlow { firstGrades to firstSummaries.dropLast(1) }
} returns resourceFlow {
Triple(
firstGrades,
firstSummaries.dropLast(1),
emptyList()
)
}
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
} returns resourceFlow { secondGrades to secondSummaries }
} returns resourceFlow { Triple(secondGrades, secondSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@ -1219,16 +1353,20 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 5.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
Triple(
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 5.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0)
),
listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0)),
emptyList()
)
}
coEvery {
gradeRepository.getGrades(
@ -1237,11 +1375,15 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
getGrade(23, "Fizyka", 4.0, modifier = 0.3, weight = 2.0)
) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
Triple(
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
getGrade(23, "Fizyka", 4.0, modifier = 0.3, weight = 2.0)
),
listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0)),
emptyList()
)
}
val items = runBlocking {
@ -1266,23 +1408,31 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 5.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
Triple(
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 5.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0)
),
listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0)),
emptyList()
)
}
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
getGrade(23, "Fizyka", 4.0, modifier = 0.3, weight = 2.0)
) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
Triple(
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
getGrade(23, "Fizyka", 4.0, modifier = 0.3, weight = 2.0)
),
listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0)),
emptyList()
)
}
val items = runBlocking {
@ -1313,23 +1463,31 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 5.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
Triple(
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 5.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0)
),
listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0)),
emptyList()
)
}
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
Triple(
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
),
listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0)),
emptyList()
)
}
val items = runBlocking {
@ -1366,16 +1524,20 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 5.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
Triple(
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 5.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 4.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0)
),
listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0)),
emptyList()
)
}
coEvery {
gradeRepository.getGrades(
@ -1384,11 +1546,15 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
Triple(
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
),
listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0)),
emptyList()
)
}
val items = runBlocking {
@ -1413,9 +1579,9 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns
resourceFlow { firstGrades to firstSummaries }
resourceFlow { Triple(firstGrades, firstSummaries, emptyList()) }
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns
resourceFlow { listOf<Grade>() to firstSummaries }
resourceFlow { Triple(listOf<Grade>(), firstSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(