mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-31 12:58:21 +01:00
Add information about student in grade statistics pie chart (#1749)
Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
This commit is contained in:
parent
d3bf5c3e0a
commit
aff0fb3a60
@ -73,6 +73,8 @@ android {
|
||||
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
shrinkResources false
|
||||
resValue "string", "app_name", "Wulkanowy DEV"
|
||||
applicationIdSuffix ".dev"
|
||||
versionNameSuffix "-dev"
|
||||
|
@ -24,5 +24,8 @@ data class GradeSemesterStatistics(
|
||||
var id: Long = 0
|
||||
|
||||
@Transient
|
||||
var average: String = ""
|
||||
var classAverage: String = ""
|
||||
|
||||
@Transient
|
||||
var studentAverage: String = ""
|
||||
}
|
||||
|
@ -63,20 +63,16 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
mapResult = { items ->
|
||||
when (subjectName) {
|
||||
"Wszystkie" -> {
|
||||
val numerator = items.map {
|
||||
it.classAverage.replace(",", ".").toDoubleOrNull() ?: .0
|
||||
}.filterNot { it == .0 }
|
||||
(items.reversed() + GradePartialStatistics(
|
||||
val summaryItem = GradePartialStatistics(
|
||||
studentId = semester.studentId,
|
||||
semesterId = semester.semesterId,
|
||||
subject = subjectName,
|
||||
classAverage = if (numerator.isEmpty()) "" else numerator.average().let {
|
||||
"%.2f".format(Locale.FRANCE, it)
|
||||
},
|
||||
studentAverage = "",
|
||||
classAverage = items.map { it.classAverage }.getSummaryAverage(),
|
||||
studentAverage = items.map { it.studentAverage }.getSummaryAverage(),
|
||||
classAmounts = items.map { it.classAmounts }.sumGradeAmounts(),
|
||||
studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts()
|
||||
)).reversed()
|
||||
)
|
||||
listOf(summaryItem) + items
|
||||
}
|
||||
else -> items.filter { it.subject == subjectName }
|
||||
}.mapPartialToStatisticItems()
|
||||
@ -112,29 +108,29 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
val itemsWithAverage = items.map { item ->
|
||||
item.copy().apply {
|
||||
val denominator = item.amounts.sum()
|
||||
average = if (denominator == 0) "" else {
|
||||
classAverage = if (denominator == 0) "" else {
|
||||
(item.amounts.mapIndexed { gradeValue, amount ->
|
||||
(gradeValue + 1) * amount
|
||||
}.sum().toDouble() / denominator).let {
|
||||
"%.2f".format(Locale.FRANCE, it)
|
||||
}
|
||||
}.sum().toDouble() / denominator).asAverageString()
|
||||
}
|
||||
}
|
||||
}
|
||||
when (subjectName) {
|
||||
"Wszystkie" -> (itemsWithAverage.reversed() + GradeSemesterStatistics(
|
||||
"Wszystkie" -> {
|
||||
val summaryItem = GradeSemesterStatistics(
|
||||
studentId = semester.studentId,
|
||||
semesterId = semester.semesterId,
|
||||
subject = subjectName,
|
||||
amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(),
|
||||
studentGrade = 0
|
||||
studentGrade = 0,
|
||||
).apply {
|
||||
average = itemsWithAverage.mapNotNull {
|
||||
it.average.replace(",", ".").toDoubleOrNull()
|
||||
}.average().let {
|
||||
"%.2f".format(Locale.FRANCE, it)
|
||||
classAverage = itemsWithAverage.map { it.classAverage }.getSummaryAverage()
|
||||
studentAverage = items
|
||||
.mapNotNull { summary -> summary.studentGrade.takeIf { it != 0 } }
|
||||
.average().asAverageString()
|
||||
}
|
||||
listOf(summaryItem) + itemsWithAverage
|
||||
}
|
||||
}).reversed()
|
||||
else -> itemsWithAverage.filter { it.subject == subjectName }
|
||||
}.mapSemesterToStatisticItems()
|
||||
}
|
||||
@ -171,6 +167,19 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
}
|
||||
)
|
||||
|
||||
private fun List<String>.getSummaryAverage(): String {
|
||||
val averages = mapNotNull {
|
||||
it.replace(",", ".").toDoubleOrNull()
|
||||
}
|
||||
|
||||
return averages.average()
|
||||
.asAverageString()
|
||||
.takeIf { averages.isNotEmpty() }
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
private fun Double.asAverageString(): String = "%.2f".format(Locale.FRANCE, this)
|
||||
|
||||
private fun List<List<Int>>.sumGradeAmounts(): List<Int> {
|
||||
val result = mutableListOf(0, 0, 0, 0, 0, 0)
|
||||
forEach {
|
||||
|
@ -9,12 +9,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.mikephil.charting.components.Legend
|
||||
import com.github.mikephil.charting.components.LegendEntry
|
||||
import com.github.mikephil.charting.data.BarData
|
||||
import com.github.mikephil.charting.data.BarDataSet
|
||||
import com.github.mikephil.charting.data.BarEntry
|
||||
import com.github.mikephil.charting.data.PieData
|
||||
import com.github.mikephil.charting.data.PieDataSet
|
||||
import com.github.mikephil.charting.data.PieEntry
|
||||
import com.github.mikephil.charting.data.*
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
||||
@ -136,20 +131,50 @@ class GradeStatisticsAdapter @Inject constructor() :
|
||||
binding: ItemGradeStatisticsPieBinding,
|
||||
partials: GradePartialStatistics
|
||||
) {
|
||||
bindPieChart(binding, partials.subject, partials.classAverage, partials.classAmounts)
|
||||
val studentAverage = partials.studentAverage.takeIf { it.isNotEmpty() }?.let {
|
||||
binding.root.context.getString(R.string.grade_statistics_student_average, it)
|
||||
}
|
||||
bindPieChart(
|
||||
binding = binding,
|
||||
subject = partials.subject,
|
||||
average = partials.classAverage,
|
||||
studentValue = studentAverage,
|
||||
amounts = partials.classAmounts
|
||||
)
|
||||
}
|
||||
|
||||
private fun bindSemesterChart(
|
||||
binding: ItemGradeStatisticsPieBinding,
|
||||
semester: GradeSemesterStatistics
|
||||
) {
|
||||
bindPieChart(binding, semester.subject, semester.average, semester.amounts)
|
||||
val studentAverage = semester.studentAverage.takeIf { it.isNotBlank() }
|
||||
val studentGrade = semester.studentGrade.takeIf { it != 0 }
|
||||
|
||||
val studentValue = when {
|
||||
studentAverage != null -> binding.root.context.getString(
|
||||
R.string.grade_statistics_student_average,
|
||||
studentAverage
|
||||
)
|
||||
studentGrade != null -> binding.root.context.getString(
|
||||
R.string.grade_statistics_student_grade,
|
||||
studentGrade.toString()
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
bindPieChart(
|
||||
binding = binding,
|
||||
subject = semester.subject,
|
||||
average = semester.classAverage,
|
||||
studentValue = studentValue,
|
||||
amounts = semester.amounts
|
||||
)
|
||||
}
|
||||
|
||||
private fun bindPieChart(
|
||||
binding: ItemGradeStatisticsPieBinding,
|
||||
subject: String,
|
||||
average: String,
|
||||
studentValue: String?,
|
||||
amounts: List<Int>
|
||||
) {
|
||||
with(binding.gradeStatisticsPieTitle) {
|
||||
@ -208,13 +233,13 @@ class GradeStatisticsAdapter @Inject constructor() :
|
||||
val numberOfGradesString = amounts.fold(0) { acc, it -> acc + it }
|
||||
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
|
||||
val averageString =
|
||||
binding.root.context.getString(R.string.grade_statistics_average, average)
|
||||
binding.root.context.getString(R.string.grade_statistics_class_average, average)
|
||||
|
||||
minAngleForSlices = 25f
|
||||
description.isEnabled = false
|
||||
centerText =
|
||||
numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() }
|
||||
.orEmpty()
|
||||
.orEmpty() + studentValue?.let { "\n$it" }.orEmpty()
|
||||
|
||||
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
|
||||
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
|
||||
|
@ -105,7 +105,7 @@
|
||||
<string name="grade_statistics_semester">Semestr</string>
|
||||
<string name="grade_statistics_points">Body</string>
|
||||
<string name="grade_statistics_legend">Vysvětlivky</string>
|
||||
<string name="grade_statistics_average">Průměr: %1$s</string>
|
||||
<string name="grade_statistics_class_average">Průměr: %1$s</string>
|
||||
<string name="grade_statistics_average_class">Třída</string>
|
||||
<string name="grade_statistics_average_student">Žák</string>
|
||||
<plurals name="grade_number_item">
|
||||
|
@ -105,7 +105,7 @@
|
||||
<string name="grade_statistics_semester">Semester</string>
|
||||
<string name="grade_statistics_points">Punkte</string>
|
||||
<string name="grade_statistics_legend">Legende</string>
|
||||
<string name="grade_statistics_average">Durchschnitt: %1$s</string>
|
||||
<string name="grade_statistics_class_average">Durchschnitt: %1$s</string>
|
||||
<string name="grade_statistics_average_class">Klasse</string>
|
||||
<string name="grade_statistics_average_student">Schüler</string>
|
||||
<plurals name="grade_number_item">
|
||||
|
@ -105,7 +105,7 @@
|
||||
<string name="grade_statistics_semester">Semestralne</string>
|
||||
<string name="grade_statistics_points">Punkty</string>
|
||||
<string name="grade_statistics_legend">Legenda</string>
|
||||
<string name="grade_statistics_average">Średnia: %1$s</string>
|
||||
<string name="grade_statistics_class_average">Średnia: %1$s</string>
|
||||
<string name="grade_statistics_average_class">Klasa</string>
|
||||
<string name="grade_statistics_average_student">Uczeń</string>
|
||||
<plurals name="grade_number_item">
|
||||
|
@ -105,7 +105,7 @@
|
||||
<string name="grade_statistics_semester">За семестр</string>
|
||||
<string name="grade_statistics_points">Баллы</string>
|
||||
<string name="grade_statistics_legend">Легенда</string>
|
||||
<string name="grade_statistics_average">Средняя: %1$s</string>
|
||||
<string name="grade_statistics_class_average">Средняя: %1$s</string>
|
||||
<string name="grade_statistics_average_class">Класс</string>
|
||||
<string name="grade_statistics_average_student">Студент</string>
|
||||
<plurals name="grade_number_item">
|
||||
|
@ -105,7 +105,7 @@
|
||||
<string name="grade_statistics_semester">Semester</string>
|
||||
<string name="grade_statistics_points">Body</string>
|
||||
<string name="grade_statistics_legend">Vysvetlivky</string>
|
||||
<string name="grade_statistics_average">Priemer: %1$s</string>
|
||||
<string name="grade_statistics_class_average">Priemer: %1$s</string>
|
||||
<string name="grade_statistics_average_class">Trieda</string>
|
||||
<string name="grade_statistics_average_student">Žiák</string>
|
||||
<plurals name="grade_number_item">
|
||||
|
@ -105,7 +105,7 @@
|
||||
<string name="grade_statistics_semester">Семестрові</string>
|
||||
<string name="grade_statistics_points">Бали</string>
|
||||
<string name="grade_statistics_legend">Умовні позначення</string>
|
||||
<string name="grade_statistics_average">Середня оцінка: %1$s</string>
|
||||
<string name="grade_statistics_class_average">Середня оцінка: %1$s</string>
|
||||
<string name="grade_statistics_average_class">Клас</string>
|
||||
<string name="grade_statistics_average_student">Учень</string>
|
||||
<plurals name="grade_number_item">
|
||||
|
@ -115,7 +115,9 @@
|
||||
<string name="grade_statistics_semester">Semester</string>
|
||||
<string name="grade_statistics_points">Points</string>
|
||||
<string name="grade_statistics_legend">Legend</string>
|
||||
<string name="grade_statistics_average">Average: %1$s</string>
|
||||
<string name="grade_statistics_class_average">Class average: %1$s</string>
|
||||
<string name="grade_statistics_student_average">Your average: %1$s</string>
|
||||
<string name="grade_statistics_student_grade">Your grade: %1$s</string>
|
||||
<string name="grade_statistics_average_class">Class</string>
|
||||
<string name="grade_statistics_average_student">Student</string>
|
||||
<plurals name="grade_number_item">
|
||||
|
@ -11,14 +11,9 @@ import io.github.wulkanowy.sdk.pojo.GradeStatisticsItem
|
||||
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.toFirstResult
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.*
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
@ -48,22 +43,27 @@ class GradeStatisticsRepositoryTest {
|
||||
|
||||
private lateinit var gradeStatisticsRepository: GradeStatisticsRepository
|
||||
|
||||
private val remotePartialList = listOf(
|
||||
getGradeStatisticsPartialSubject("Fizyka"),
|
||||
getGradeStatisticsPartialSubject("Matematyka")
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
every { refreshHelper.shouldBeRefreshed(any()) } returns false
|
||||
|
||||
gradeStatisticsRepository = GradeStatisticsRepository(gradePartialStatisticsDb, gradePointsStatisticsDb, gradeSemesterStatisticsDb, sdk, refreshHelper)
|
||||
gradeStatisticsRepository = GradeStatisticsRepository(
|
||||
gradePartialStatisticsDb = gradePartialStatisticsDb,
|
||||
gradePointsStatisticsDb = gradePointsStatisticsDb,
|
||||
gradeSemesterStatisticsDb = gradeSemesterStatisticsDb,
|
||||
sdk = sdk,
|
||||
refreshHelper = refreshHelper,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `force refresh without difference`() {
|
||||
// prepare
|
||||
val remotePartialList = listOf(
|
||||
getGradeStatisticsPartialSubject("Fizyka"),
|
||||
getGradeStatisticsPartialSubject("Matematyka")
|
||||
)
|
||||
coEvery { sdk.getGradesPartialStatistics(1) } returns remotePartialList
|
||||
coEvery { gradePartialStatisticsDb.loadAll(1, 1) } returnsMany listOf(
|
||||
flowOf(remotePartialList.mapToEntities(semester)),
|
||||
@ -73,21 +73,74 @@ class GradeStatisticsRepositoryTest {
|
||||
coEvery { gradePartialStatisticsDb.deleteAll(any()) } just Runs
|
||||
|
||||
// execute
|
||||
val res = runBlocking { gradeStatisticsRepository.getGradesPartialStatistics(student, semester, "Wszystkie", true).toFirstResult() }
|
||||
val res = runBlocking {
|
||||
gradeStatisticsRepository.getGradesPartialStatistics(
|
||||
student = student,
|
||||
semester = semester,
|
||||
subjectName = "Wszystkie",
|
||||
forceRefresh = true,
|
||||
).toFirstResult()
|
||||
}
|
||||
val items = res.data.orEmpty()
|
||||
|
||||
// verify
|
||||
assertEquals(null, res.error)
|
||||
assertEquals(2 + 1, res.data?.size)
|
||||
assertEquals("", items[0].partial?.studentAverage)
|
||||
assertEquals("", items[1].partial?.studentAverage)
|
||||
assertEquals("", items[2].partial?.studentAverage)
|
||||
coVerify { sdk.getGradesPartialStatistics(1) }
|
||||
coVerify { gradePartialStatisticsDb.loadAll(1, 1) }
|
||||
coVerify { gradePartialStatisticsDb.insertAll(match { it.isEmpty() }) }
|
||||
coVerify { gradePartialStatisticsDb.deleteAll(match { it.isEmpty() }) }
|
||||
}
|
||||
|
||||
private fun getGradeStatisticsPartialSubject(subjectName: String) = GradeStatisticsSubject(
|
||||
@Test
|
||||
fun `force refresh without difference with filled up items`() {
|
||||
// prepare
|
||||
val remotePartialList = listOf(
|
||||
getGradeStatisticsPartialSubject("Fizyka", "1.0"),
|
||||
getGradeStatisticsPartialSubject("Matematyka", "5.0")
|
||||
)
|
||||
coEvery { sdk.getGradesPartialStatistics(1) } returns remotePartialList
|
||||
coEvery { gradePartialStatisticsDb.loadAll(1, 1) } returnsMany listOf(
|
||||
flowOf(remotePartialList.mapToEntities(semester)),
|
||||
flowOf(remotePartialList.mapToEntities(semester))
|
||||
)
|
||||
coEvery { gradePartialStatisticsDb.insertAll(any()) } returns listOf(1, 2, 3)
|
||||
coEvery { gradePartialStatisticsDb.deleteAll(any()) } just Runs
|
||||
|
||||
// execute
|
||||
val res = runBlocking {
|
||||
gradeStatisticsRepository.getGradesPartialStatistics(
|
||||
student = student,
|
||||
semester = semester,
|
||||
subjectName = "Wszystkie",
|
||||
forceRefresh = true,
|
||||
).toFirstResult()
|
||||
}
|
||||
val items = res.data.orEmpty()
|
||||
|
||||
// verify
|
||||
assertEquals(null, res.error)
|
||||
assertEquals(2 + 1, res.data?.size)
|
||||
assertEquals("3,00", items[0].partial?.studentAverage)
|
||||
assertEquals("1.0", items[1].partial?.studentAverage)
|
||||
assertEquals("5.0", items[2].partial?.studentAverage)
|
||||
coVerify { sdk.getGradesPartialStatistics(1) }
|
||||
coVerify { gradePartialStatisticsDb.loadAll(1, 1) }
|
||||
coVerify { gradePartialStatisticsDb.insertAll(match { it.isEmpty() }) }
|
||||
coVerify { gradePartialStatisticsDb.deleteAll(match { it.isEmpty() }) }
|
||||
}
|
||||
|
||||
private fun getGradeStatisticsPartialSubject(
|
||||
subjectName: String,
|
||||
studentAverage: String = "",
|
||||
classAverage: String = "",
|
||||
) = GradeStatisticsSubject(
|
||||
subject = subjectName,
|
||||
studentAverage = "",
|
||||
classAverage = "",
|
||||
studentAverage = studentAverage,
|
||||
classAverage = classAverage,
|
||||
classItems = listOf(
|
||||
GradeStatisticsItem(
|
||||
subject = subjectName,
|
||||
|
Loading…
x
Reference in New Issue
Block a user