mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-18 21:06:45 -06:00
Make GradeAverageProvider reactive to configuration changes (#1698)
Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
This commit is contained in:
parent
a2a31df98e
commit
b3c6e2004b
@ -2,9 +2,11 @@ package io.github.wulkanowy.data.repositories
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.edit
|
||||
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
||||
import com.fredporciuncula.flow.preferences.Preference
|
||||
import com.fredporciuncula.flow.preferences.Serializer
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.enums.*
|
||||
@ -35,20 +37,29 @@ class PreferencesRepository @Inject constructor(
|
||||
R.bool.pref_default_attendance_present
|
||||
)
|
||||
|
||||
val gradeAverageMode: GradeAverageMode
|
||||
get() = GradeAverageMode.getByValue(
|
||||
getString(
|
||||
R.string.pref_key_grade_average_mode,
|
||||
R.string.pref_default_grade_average_mode
|
||||
)
|
||||
private val gradeAverageModePref: Preference<GradeAverageMode>
|
||||
get() = getObjectFlow(
|
||||
R.string.pref_key_grade_average_mode,
|
||||
R.string.pref_default_grade_average_mode,
|
||||
object : Serializer<GradeAverageMode> {
|
||||
override fun serialize(value: GradeAverageMode) = value.value
|
||||
override fun deserialize(serialized: String) =
|
||||
GradeAverageMode.getByValue(serialized)
|
||||
},
|
||||
)
|
||||
|
||||
val gradeAverageForceCalc: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_grade_average_force_calc,
|
||||
R.bool.pref_default_grade_average_force_calc
|
||||
val gradeAverageModeFlow: Flow<GradeAverageMode>
|
||||
get() = gradeAverageModePref.asFlow()
|
||||
|
||||
private val gradeAverageForceCalcPref: Preference<Boolean>
|
||||
get() = flowSharedPref.getBoolean(
|
||||
context.getString(R.string.pref_key_grade_average_force_calc),
|
||||
context.resources.getBoolean(R.bool.pref_default_grade_average_force_calc)
|
||||
)
|
||||
|
||||
val gradeAverageForceCalcFlow: Flow<Boolean>
|
||||
get() = gradeAverageForceCalcPref.asFlow()
|
||||
|
||||
val gradeExpandMode: GradeExpandMode
|
||||
get() = GradeExpandMode.getByValue(
|
||||
getString(
|
||||
@ -138,12 +149,24 @@ class PreferencesRepository @Inject constructor(
|
||||
R.string.pref_default_grade_modifier_plus
|
||||
).toDouble()
|
||||
|
||||
val gradePlusModifierFlow: Flow<Double>
|
||||
get() = getStringFlow(
|
||||
R.string.pref_key_grade_modifier_plus,
|
||||
R.string.pref_default_grade_modifier_plus
|
||||
).asFlow().map { it.toDouble() }
|
||||
|
||||
val gradeMinusModifier: Double
|
||||
get() = getString(
|
||||
R.string.pref_key_grade_modifier_minus,
|
||||
R.string.pref_default_grade_modifier_minus
|
||||
).toDouble()
|
||||
|
||||
val gradeMinusModifierFlow: Flow<Double>
|
||||
get() = getStringFlow(
|
||||
R.string.pref_key_grade_modifier_minus,
|
||||
R.string.pref_default_grade_modifier_minus
|
||||
).asFlow().map { it.toDouble() }
|
||||
|
||||
val fillMessageContent: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_fill_message_content,
|
||||
@ -191,11 +214,11 @@ class PreferencesRepository @Inject constructor(
|
||||
R.bool.pref_default_subjects_without_grades
|
||||
)
|
||||
|
||||
val isOptionalArithmeticAverage: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_optional_arithmetic_average,
|
||||
R.bool.pref_default_optional_arithmetic_average
|
||||
)
|
||||
val isOptionalArithmeticAverageFlow: Flow<Boolean>
|
||||
get() = flowSharedPref.getBoolean(
|
||||
context.getString(R.string.pref_key_optional_arithmetic_average),
|
||||
context.resources.getBoolean(R.bool.pref_default_optional_arithmetic_average)
|
||||
).asFlow()
|
||||
|
||||
var lasSyncDate: Instant?
|
||||
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
|
||||
@ -342,6 +365,21 @@ class PreferencesRepository @Inject constructor(
|
||||
private fun getLong(id: String, default: Int) =
|
||||
sharedPref.getLong(id, context.resources.getString(default).toLong())
|
||||
|
||||
private fun getStringFlow(id: Int, default: Int) =
|
||||
flowSharedPref.getString(context.getString(id), context.getString(default))
|
||||
|
||||
private fun <T : Any> getObjectFlow(
|
||||
@StringRes id: Int,
|
||||
@StringRes default: Int,
|
||||
serializer: Serializer<T>
|
||||
): Preference<T> = flowSharedPref.getObject(
|
||||
key = context.getString(id),
|
||||
serializer = serializer,
|
||||
defaultValue = serializer.deserialize(
|
||||
flowSharedPref.getString(context.getString(default)).get()
|
||||
)
|
||||
)
|
||||
|
||||
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
||||
|
||||
private fun getString(id: String, default: Int) =
|
||||
|
@ -12,70 +12,91 @@ import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import javax.inject.Inject
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class GradeAverageProvider @Inject constructor(
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val gradeRepository: GradeRepository,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) {
|
||||
|
||||
private val plusModifier get() = preferencesRepository.gradePlusModifier
|
||||
private data class AverageCalcParams(
|
||||
val gradeAverageMode: GradeAverageMode,
|
||||
val forceAverageCalc: Boolean,
|
||||
val isOptionalArithmeticAverage: Boolean,
|
||||
val plusModifier: Double,
|
||||
val minusModifier: Double,
|
||||
)
|
||||
|
||||
private val minusModifier get() = preferencesRepository.gradeMinusModifier
|
||||
|
||||
private val isOptionalArithmeticAverage get() = preferencesRepository.isOptionalArithmeticAverage
|
||||
|
||||
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) =
|
||||
fun getGradesDetailsWithAverage(
|
||||
student: Student,
|
||||
semesterId: Int,
|
||||
forceRefresh: Boolean
|
||||
): Flow<Resource<List<GradeSubject>>> = combine(
|
||||
flow = preferencesRepository.gradeAverageModeFlow,
|
||||
flow2 = preferencesRepository.gradeAverageForceCalcFlow,
|
||||
flow3 = preferencesRepository.isOptionalArithmeticAverageFlow,
|
||||
flow4 = preferencesRepository.gradePlusModifierFlow,
|
||||
flow5 = preferencesRepository.gradeMinusModifierFlow,
|
||||
) { gradeAverageMode, forceAverageCalc, isOptionalArithmeticAverage, plusModifier, minusModifier ->
|
||||
AverageCalcParams(
|
||||
gradeAverageMode = gradeAverageMode,
|
||||
forceAverageCalc = forceAverageCalc,
|
||||
isOptionalArithmeticAverage = isOptionalArithmeticAverage,
|
||||
plusModifier = plusModifier,
|
||||
minusModifier = minusModifier,
|
||||
)
|
||||
}.flatMapLatest { params ->
|
||||
flatResourceFlow {
|
||||
val semesters = semesterRepository.getSemesters(student)
|
||||
|
||||
when (preferencesRepository.gradeAverageMode) {
|
||||
when (params.gradeAverageMode) {
|
||||
ONE_SEMESTER -> getGradeSubjects(
|
||||
student = student,
|
||||
semester = semesters.single { it.semesterId == semesterId },
|
||||
forceRefresh = forceRefresh
|
||||
forceRefresh = forceRefresh,
|
||||
params = params,
|
||||
)
|
||||
BOTH_SEMESTERS -> calculateCombinedAverage(
|
||||
student = student,
|
||||
semesters = semesters,
|
||||
semesterId = semesterId,
|
||||
forceRefresh = forceRefresh,
|
||||
averageMode = BOTH_SEMESTERS
|
||||
config = params,
|
||||
)
|
||||
ALL_YEAR -> calculateCombinedAverage(
|
||||
student = student,
|
||||
semesters = semesters,
|
||||
semesterId = semesterId,
|
||||
forceRefresh = forceRefresh,
|
||||
averageMode = ALL_YEAR
|
||||
config = params,
|
||||
)
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateCombinedAverage(
|
||||
student: Student,
|
||||
semesters: List<Semester>,
|
||||
semesterId: Int,
|
||||
forceRefresh: Boolean,
|
||||
averageMode: GradeAverageMode
|
||||
config: AverageCalcParams,
|
||||
): Flow<Resource<List<GradeSubject>>> {
|
||||
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester =
|
||||
semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
|
||||
val selectedSemesterGradeSubjects =
|
||||
getGradeSubjects(student, selectedSemester, forceRefresh)
|
||||
getGradeSubjects(student, selectedSemester, forceRefresh, config)
|
||||
|
||||
if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects
|
||||
|
||||
val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh)
|
||||
val firstSemesterGradeSubjects =
|
||||
getGradeSubjects(student, firstSemester, forceRefresh, config)
|
||||
|
||||
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
|
||||
if (firstSemesterGradeSubject.errorOrNull != null) {
|
||||
@ -91,21 +112,21 @@ class GradeAverageProvider @Inject constructor(
|
||||
val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty()
|
||||
.singleOrNull { it.subject == secondSemesterSubject.subject }
|
||||
|
||||
val updatedAverage = if (averageMode == ALL_YEAR) {
|
||||
val updatedAverage = if (config.gradeAverageMode == ALL_YEAR) {
|
||||
calculateAllYearAverage(
|
||||
student = student,
|
||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||
isGradeAverageForceCalc = isGradeAverageForceCalc,
|
||||
secondSemesterSubject = secondSemesterSubject,
|
||||
firstSemesterSubject = firstSemesterSubject
|
||||
firstSemesterSubject = firstSemesterSubject,
|
||||
config = config,
|
||||
)
|
||||
} else {
|
||||
calculateBothSemestersAverage(
|
||||
student = student,
|
||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||
isGradeAverageForceCalc = isGradeAverageForceCalc,
|
||||
secondSemesterSubject = secondSemesterSubject,
|
||||
firstSemesterSubject = firstSemesterSubject
|
||||
firstSemesterSubject = firstSemesterSubject,
|
||||
config = config
|
||||
)
|
||||
}
|
||||
secondSemesterSubject.copy(average = updatedAverage)
|
||||
@ -117,17 +138,17 @@ class GradeAverageProvider @Inject constructor(
|
||||
private fun calculateAllYearAverage(
|
||||
student: Student,
|
||||
isAnyVulcanAverage: Boolean,
|
||||
isGradeAverageForceCalc: Boolean,
|
||||
secondSemesterSubject: GradeSubject,
|
||||
firstSemesterSubject: GradeSubject?
|
||||
) = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
|
||||
val updatedSecondSemesterGrades =
|
||||
secondSemesterSubject.grades.updateModifiers(student)
|
||||
val updatedFirstSemesterGrades =
|
||||
firstSemesterSubject?.grades?.updateModifiers(student).orEmpty()
|
||||
firstSemesterSubject: GradeSubject?,
|
||||
config: AverageCalcParams,
|
||||
) = if (!isAnyVulcanAverage || config.forceAverageCalc) {
|
||||
val updatedSecondSemesterGrades = secondSemesterSubject.grades
|
||||
.updateModifiers(student, config)
|
||||
val updatedFirstSemesterGrades = firstSemesterSubject?.grades
|
||||
?.updateModifiers(student, config).orEmpty()
|
||||
|
||||
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
|
||||
isOptionalArithmeticAverage
|
||||
config.isOptionalArithmeticAverage
|
||||
)
|
||||
} else {
|
||||
secondSemesterSubject.average
|
||||
@ -136,32 +157,35 @@ class GradeAverageProvider @Inject constructor(
|
||||
private fun calculateBothSemestersAverage(
|
||||
student: Student,
|
||||
isAnyVulcanAverage: Boolean,
|
||||
isGradeAverageForceCalc: Boolean,
|
||||
secondSemesterSubject: GradeSubject,
|
||||
firstSemesterSubject: GradeSubject?
|
||||
): Double = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
|
||||
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
||||
firstSemesterSubject: GradeSubject?,
|
||||
config: AverageCalcParams,
|
||||
): Double {
|
||||
return if (!isAnyVulcanAverage || config.forceAverageCalc) {
|
||||
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
||||
val secondSemesterAverage = secondSemesterSubject.grades
|
||||
.updateModifiers(student, config)
|
||||
.calcAverage(config.isOptionalArithmeticAverage)
|
||||
val firstSemesterAverage = firstSemesterSubject?.grades
|
||||
?.updateModifiers(student, config)
|
||||
?.calcAverage(config.isOptionalArithmeticAverage) ?: secondSemesterAverage
|
||||
|
||||
val secondSemesterAverage = secondSemesterSubject.grades.updateModifiers(student)
|
||||
.calcAverage(isOptionalArithmeticAverage)
|
||||
val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
|
||||
?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage
|
||||
(secondSemesterAverage + firstSemesterAverage) / divider
|
||||
} else {
|
||||
val divider = if (secondSemesterSubject.average > 0) 2 else 1
|
||||
|
||||
(secondSemesterAverage + firstSemesterAverage) / divider
|
||||
} else {
|
||||
val divider = if (secondSemesterSubject.average > 0) 2 else 1
|
||||
|
||||
(secondSemesterSubject.average + (firstSemesterSubject?.average
|
||||
?: secondSemesterSubject.average)) / divider
|
||||
secondSemesterSubject.average.plus(
|
||||
(firstSemesterSubject?.average ?: secondSemesterSubject.average)
|
||||
) / divider
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGradeSubjects(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean
|
||||
forceRefresh: Boolean,
|
||||
params: AverageCalcParams,
|
||||
): Flow<Resource<List<GradeSubject>>> {
|
||||
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
|
||||
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
|
||||
.mapResourceData { res ->
|
||||
val (details, summaries) = res
|
||||
@ -172,13 +196,15 @@ class GradeAverageProvider @Inject constructor(
|
||||
student = student,
|
||||
semester = semester,
|
||||
grades = allGrades.toList(),
|
||||
calcAverage = isAnyAverage
|
||||
calcAverage = isAnyAverage,
|
||||
params = params,
|
||||
).map { summary ->
|
||||
val grades = allGrades[summary.subject].orEmpty()
|
||||
GradeSubject(
|
||||
subject = summary.subject,
|
||||
average = if (!isAnyAverage || isGradeAverageForceCalc) {
|
||||
grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage)
|
||||
average = if (!isAnyAverage || params.forceAverageCalc) {
|
||||
grades.updateModifiers(student, params)
|
||||
.calcAverage(params.isOptionalArithmeticAverage)
|
||||
} else summary.average,
|
||||
points = summary.pointsSum,
|
||||
summary = summary,
|
||||
@ -195,7 +221,8 @@ class GradeAverageProvider @Inject constructor(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
grades: List<Pair<String, List<Grade>>>,
|
||||
calcAverage: Boolean
|
||||
calcAverage: Boolean,
|
||||
params: AverageCalcParams,
|
||||
): List<GradeSummary> {
|
||||
if (isNotEmpty() && size > grades.size) return this
|
||||
|
||||
@ -211,15 +238,16 @@ class GradeAverageProvider @Inject constructor(
|
||||
proposedPoints = "",
|
||||
finalPoints = "",
|
||||
pointsSum = "",
|
||||
average = if (calcAverage) details.updateModifiers(student)
|
||||
.calcAverage(isOptionalArithmeticAverage) else .0
|
||||
average = if (calcAverage) details.updateModifiers(student, params)
|
||||
.calcAverage(params.isOptionalArithmeticAverage) else .0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Grade>.updateModifiers(student: Student): List<Grade> {
|
||||
return if (student.loginMode == Sdk.Mode.SCRAPPER.name) {
|
||||
map { it.changeModifier(plusModifier, minusModifier) }
|
||||
} else this
|
||||
}
|
||||
private fun List<Grade>.updateModifiers(
|
||||
student: Student,
|
||||
params: AverageCalcParams,
|
||||
): List<Grade> = if (student.loginMode == Sdk.Mode.SCRAPPER.name) {
|
||||
map { it.changeModifier(params.plusModifier, params.minusModifier) }
|
||||
} else this
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user