Add calculated average help dialog (#1379)

Co-authored-by: Rafał Borcz <RafalBO99@outlook.com>
This commit is contained in:
Piotr Romanowski 2021-09-25 17:19:21 +02:00 committed by GitHub
parent f9e0f7b390
commit 7a46ef5f19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 139 additions and 44 deletions

View File

@ -131,7 +131,9 @@ class GradeAverageProvider @Inject constructor(
val updatedFirstSemesterGrades =
firstSemesterSubject?.grades?.updateModifiers(student).orEmpty()
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(isOptionalArithmeticAverage)
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
isOptionalArithmeticAverage
)
} else {
secondSemesterSubject.average
}
@ -147,7 +149,8 @@ class GradeAverageProvider @Inject constructor(
return if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
val secondSemesterAverage =
secondSemesterSubject.grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage)
secondSemesterSubject.grades.updateModifiers(student)
.calcAverage(isOptionalArithmeticAverage)
val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage
@ -213,7 +216,8 @@ class GradeAverageProvider @Inject constructor(
proposedPoints = "",
finalPoints = "",
pointsSum = "",
average = if (calcAverage) details.updateModifiers(student).calcAverage(isOptionalArithmeticAverage) else .0
average = if (calcAverage) details.updateModifiers(student)
.calcAverage(isOptionalArithmeticAverage) else .0
)
}
}

View File

@ -10,7 +10,7 @@ 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.utils.calcAverage
import io.github.wulkanowy.utils.calcFinalAverage
import java.util.Locale
import javax.inject.Inject
@ -25,6 +25,10 @@ class GradeSummaryAdapter @Inject constructor(
var items = emptyList<GradeSummary>()
var onCalculatedHelpClickListener: () -> Unit = {}
var onFinalHelpClickListener: () -> Unit = {}
override fun getItemCount() = items.size + if (items.isNotEmpty()) 1 else 0
override fun getItemViewType(position: Int) = when (position) {
@ -60,7 +64,7 @@ class GradeSummaryAdapter @Inject constructor(
val finalItemsCount = items.count { it.finalGrade.matches("[0-6][+-]?".toRegex()) }
val calculatedItemsCount = items.count { value -> value.average != 0.0 }
val allItemsCount = items.count { !it.subject.equals("zachowanie", true) }
val finalAverage = items.calcAverage(
val finalAverage = items.calcFinalAverage(
preferencesRepository.gradePlusModifier,
preferencesRepository.gradeMinusModifier
)
@ -83,6 +87,9 @@ class GradeSummaryAdapter @Inject constructor(
calculatedItemsCount,
allItemsCount
)
gradeSummaryCalculatedAverageHelp.setOnClickListener { onCalculatedHelpClickListener() }
gradeSummaryFinalAverageHelp.setOnClickListener { onFinalHelpClickListener() }
}
}

View File

@ -5,6 +5,7 @@ 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 dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@ -48,6 +49,11 @@ class GradeSummaryFragment :
}
override fun initView() {
with(gradeSummaryAdapter) {
onCalculatedHelpClickListener = presenter::onCalculatedAverageHelpClick
onFinalHelpClickListener = presenter::onFinalAverageHelpClick
}
with(binding.gradeSummaryRecycler) {
layoutManager = LinearLayoutManager(context)
adapter = gradeSummaryAdapter
@ -55,7 +61,11 @@ class GradeSummaryFragment :
with(binding) {
gradeSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh)
gradeSummarySwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
gradeSummarySwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
gradeSummarySwipe.setProgressBackgroundColorSchemeColor(
requireContext().getThemeAttrColor(
R.attr.colorSwipeRefresh
)
)
gradeSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
gradeSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
@ -107,6 +117,22 @@ class GradeSummaryFragment :
binding.gradeSummarySwipe.isRefreshing = show
}
override fun showCalculatedAverageHelpDialog() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.grade_summary_calculated_average_help_dialog_title)
.setMessage(R.string.grade_summary_calculated_average_help_dialog_message)
.setPositiveButton(R.string.all_close) { _, _ -> }
.show()
}
override fun showFinalAverageHelpDialog() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.grade_summary_final_average_help_dialog_title)
.setMessage(R.string.grade_summary_final_average_help_dialog_message)
.setPositiveButton(R.string.all_close) { _, _ -> }
.show()
}
override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) {
presenter.onParentViewLoadData(semesterId, forceRefresh)
}

View File

@ -135,6 +135,14 @@ class GradeSummaryPresenter @Inject constructor(
cancelJobs("load")
}
fun onCalculatedAverageHelpClick() {
view?.showCalculatedAverageHelpDialog()
}
fun onFinalAverageHelpClick() {
view?.showFinalAverageHelpDialog()
}
private fun createGradeSummaryItems(items: List<GradeSubject>): List<GradeSummary> {
return items
.filter { !checkEmpty(it) }

View File

@ -33,6 +33,10 @@ interface GradeSummaryView : BaseView {
fun showEmpty(show: Boolean)
fun showCalculatedAverageHelpDialog()
fun showFinalAverageHelpDialog()
fun notifyParentDataLoaded(semesterId: Int)
fun notifyParentRefresh()

View File

@ -79,12 +79,7 @@ class MessagePreviewAdapter @Inject constructor() :
val readText = when {
recipientCount > 1 -> {
context.resources.getQuantityString(
R.plurals.message_read_by,
message.readBy,
message.readBy,
recipientCount
)
context.getString(R.string.message_read_by, message.readBy, recipientCount)
}
message.readBy == 1 -> {
context.getString(R.string.message_read, context.getString(R.string.all_yes))

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.utils
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.sdk.scrapper.grades.*
import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid
fun List<Grade>.calcAverage(isOptionalArithmeticAverage: Boolean): Double {
val isArithmeticAverage = isOptionalArithmeticAverage && !any { it.weightValue != .0 }
@ -18,8 +18,7 @@ fun List<Grade>.calcAverage(isOptionalArithmeticAverage: Boolean): Double {
return if (denominator != 0.0) counter / denominator else 0.0
}
@JvmName("calcSummaryAverage")
fun List<GradeSummary>.calcAverage(plusModifier: Double, minusModifier: Double) = asSequence()
fun List<GradeSummary>.calcFinalAverage(plusModifier: Double, minusModifier: Double) = asSequence()
.mapNotNull {
if (it.finalGrade.matches("[0-6][+-]?".toRegex())) {
when {

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#000000">
<path
android:fillColor="#FF000000"
android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z"/>
</vector>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -12,7 +13,7 @@
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="5dp"
android:layout_marginEnd="15dp"
android:orientation="vertical">
<TextView
@ -24,13 +25,30 @@
android:text="@string/grade_summary_calculated_average"
android:textSize="16sp" />
<TextView
android:id="@+id/gradeSummaryScrollableHeaderCalculated"
android:layout_width="match_parent"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="21sp"
tools:text="6,00" />
android:layout_gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/gradeSummaryScrollableHeaderCalculated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="21sp"
tools:text="6,00" />
<ImageButton
android:id="@+id/gradeSummaryCalculatedAverageHelp"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@+string/grade_summary_calculated_average_help_dialog_title"
app:srcCompat="@drawable/ic_help"
app:tint="?colorOnBackground" />
</LinearLayout>
<TextView
android:id="@+id/gradeSummaryScrollableHeaderCalculatedSubjectCount"
@ -57,13 +75,30 @@
android:text="@string/grade_summary_final_average"
android:textSize="16sp" />
<TextView
android:id="@+id/gradeSummaryScrollableHeaderFinal"
android:layout_width="match_parent"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="21sp"
tools:text="6,00" />
android:layout_gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/gradeSummaryScrollableHeaderFinal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="21sp"
tools:text="6,00" />
<ImageButton
android:id="@+id/gradeSummaryFinalAverageHelp"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@+string/grade_summary_calculated_average_help_dialog_title"
app:srcCompat="@drawable/ic_help"
app:tint="?colorOnBackground" />
</LinearLayout>
<TextView
android:id="@+id/gradeSummaryScrollableHeaderFinalSubjectCount"

View File

@ -41,7 +41,7 @@
<item>Kolory ocen w dzienniku</item>
</string-array>
<string-array name="grade_average_mode_entries">
<item>Średnia ocen z drugiego semestru</item>
<item>Średnia ocen z wybranego semestru</item>
<item>Średnia średnich z obu semestrów</item>
<item>Średnia wszystkich ocen z całego roku</item>
</string-array>

View File

@ -91,7 +91,11 @@
<string name="grade_summary_final_grade">Ocena końcowa</string>
<string name="grade_summary_predicted_grade">Przewidywana ocena</string>
<string name="grade_summary_calculated_average">Obliczona średnia</string>
<string name="grade_summary_calculated_average_help_dialog_title">Jak działa Obliczona średnia?</string>
<string name="grade_summary_calculated_average_help_dialog_message">Obliczona średnia to średnia arytmetyczna wyliczona ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zalecane jest, aby wybrać odpowiednią opcję. Wynika to z tego, że sposoby obliczania średnich w szkołach różnią się. Dodatkowo jeśli twoja szkoła podaje średnią z przedmiotów na stronie dziennika aplikacja pobiera je i nie wylicza tych średnich. Można to zmienić wymuszając liczenie średniej w ustawieniach aplikacji.\n\n<b>Średnia ocen z aktualnie wybranego semestru</b>:\n1. Obliczanie średniej ważonej dla każdego przedmiotu w danym semestrze\n2. Sumowanie obliczonych średnich\n3. Obliczenie średniej arytmetycznej z zsumowanych średnich\n\n<b>Średnia średnich z obu semestrów</b>:\n1. Obliczanie średniej ważonej dla każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej z obliczonych średnich semestrów 1 i 2 dla każdego przedmiotu.\n3. Sumowanie obliczonych średnich\n4. Obliczenie średniej arytmetycznej z zsumowanych średnich\n\n<b>Średnia wszystkich ocen z całego roku:</b>\n1. Obliczanie średniej ważonej z całego roku dla każdego przedmiotu. Średnia końcowa w 1 semestrze jest bez znaczenia.\n3. Sumowanie obliczonych średnich\n4. Obliczenie średniej arytmetycznej z zsumowanych średnich</string>
<string name="grade_summary_final_average">Końcowa średnia</string>
<string name="grade_summary_final_average_help_dialog_title">Jak działa Końcowa średnia?</string>
<string name="grade_summary_final_average_help_dialog_message">Końcowa średnia to średnia arytmetyczna wyliczona ze wszystkich aktualnie dostępnych ocen końcowych w danym semestrze.\n\nSchemat obliczania składa się z następujących kroków:\n1. Zsumowanie ocen końcowych wystawionych przez nauczycieli\n2. Podzielenie przez liczbę przedmiotów, z których oceny zostały już wystawione</string>
<string name="grade_summary_from_subjects">z %1$d na %2$d przedmiotów</string>
<string name="grade_menu_summary">Podsumowanie</string>
<string name="grade_menu_statistics">Klasa</string>
@ -603,7 +607,7 @@
<!--Preferences-->
<string name="pref_view_header">Wygląd i zachowanie aplikacji</string>
<string name="pref_view_list">Domyślny widok</string>
<string name="pref_view_grade_average_mode">Obliczanie średniej końcoworocznej</string>
<string name="pref_view_grade_average_mode">Opcje średniej obliczonej</string>
<string name="pref_view_grade_average_force_calc">Wymuś obliczanie średniej przez aplikację</string>
<string name="pref_view_present">Pokazuj obecność</string>
<string name="pref_view_app_theme">Motyw</string>

View File

@ -100,8 +100,8 @@
</string-array>
<string-array name="grade_average_mode_entries">
<item>Average of grades only from the 2nd semester</item>
<item>Average of grades from both semesters</item>
<item>Average of grades only from selected semester</item>
<item>Average of averages from both semesters</item>
<item>Average of grades from the whole year</item>
</string-array>
<string-array name="grade_average_mode_values" translatable="false">

View File

@ -101,6 +101,10 @@
<string name="grade_summary_final_grade">Final grade</string>
<string name="grade_summary_predicted_grade">Predicted 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.\n3. Adding calculated averages\n4. Calculating the arithmetic average of summed averages</string>
<string name="grade_summary_final_average_help_dialog_title">How does the Final Average work?</string>
<string name="grade_summary_final_average_help_dialog_message">The Final Average is the arithmetic average calculated from all currently available final grades in the given semester.\n\nThe calculation scheme consists of the following steps:\n1. Summing up the final grades given by teachers\n2. Divide by the number of subjects that have already been graded</string>
<string name="grade_summary_final_average">Final average</string>
<string name="grade_summary_from_subjects">from %1$d of %2$d subjects</string>
<string name="grade_menu_summary">Summary</string>
@ -245,10 +249,7 @@
<string name="message_chip_only_unread">Only unread</string>
<string name="message_chip_only_with_attachments">Only with attachments</string>
<string name="message_read">Read: %s</string>
<plurals name="message_read_by">
<item quantity="one">Read by: %1$d of %2$d people</item>
<item quantity="other">Read by: %1$d of %2$d people</item>
</plurals>
<string name="message_read_by">Read by: %1$d of %2$d people</string>
<plurals name="message_number_item">
<item quantity="one">%d message</item>
<item quantity="other">%d messages</item>
@ -603,7 +604,7 @@
<!--Preferences-->
<string name="pref_view_header">App appearance &amp; behavior</string>
<string name="pref_view_list">Default view</string>
<string name="pref_view_grade_average_mode">Calculation of the end-of-year average</string>
<string name="pref_view_grade_average_mode">Calculated average options</string>
<string name="pref_view_grade_average_force_calc">Force average calculation by app</string>
<string name="pref_view_present">Show presence</string>
<string name="pref_view_app_theme">Theme</string>

View File

@ -33,13 +33,15 @@ class GradeExtensionTest {
@Test
fun calcSummaryAverage() {
assertEquals(3.5, listOf(
createGradeSummary("4"),
createGradeSummary("5+"),
createGradeSummary("5-"),
createGradeSummary("test"),
createGradeSummary("0")
).calcAverage(0.5, 0.5), 0.005)
assertEquals(
3.5, listOf(
createGradeSummary("4"),
createGradeSummary("5+"),
createGradeSummary("5-"),
createGradeSummary("test"),
createGradeSummary("0")
).calcFinalAverage(0.5, 0.5), 0.005
)
}
@Test