Compare commits

...

18 Commits
2.5.4 ... 2.5.8

Author SHA1 Message Date
4d67de8e5f Merge branch 'bugfix/2.5.8' 2024-04-25 12:45:19 +02:00
f983a23b1a Version 2.5.8 2024-04-25 12:45:09 +02:00
6a1851da13 Bump sdk to 2.5.8-SNAPSHOT 2024-04-25 09:26:47 +02:00
8dbbea2138 Merge branch 'bugfix/2.5.7' 2024-04-22 22:36:13 +02:00
f6226e6b53 Version 2.5.7 2024-04-22 22:36:05 +02:00
43d13db07c Update translations 2024-04-22 22:17:03 +02:00
82210c37e3 Display separate annual average and semester average if available (#2524)
* Add displaying year average on grade details screen

* Add displaying year average on grade summary screen

* Add displaying year average on grade summary header

* Fix tests

* Hide semester average if it is not available in grade summary item

* Add full names of summary averages labels
2024-04-22 22:03:42 +02:00
b5cc32d59f Merge branch 'bugfix/2.5.6' 2024-04-22 00:39:19 +02:00
d943d03266 Version 2.5.6 2024-04-22 00:39:04 +02:00
6eca8c42f5 Show graduated students on top of student select items list 2024-04-22 00:11:29 +02:00
af989ba9f6 Don't display brackets in login student select items when schoolShortName is blank 2024-04-21 23:59:29 +02:00
4a65a5b192 Migrate away from userLoginId to studentId due to vulcan last changes 2024-04-21 23:51:34 +02:00
bbbafdfe70 Bump sdk to 2.5.6-SNAPSHOT 2024-04-21 20:37:31 +02:00
a82e11d694 Merge branch 'bugfix/2.5.5' 2024-03-26 20:38:01 +01:00
4dc5fc65ac Version 2.5.5 2024-03-26 20:37:51 +01:00
7463cf6253 Replace function in DAO to 'OR' in SQL query 2024-03-26 20:29:35 +01:00
d799ec7ac9 Add skipping migration when previous attempt failed (#2508) 2024-03-26 19:19:44 +01:00
254719f22f Remove classId from semester query when eduOne (#2509) 2024-03-26 19:02:35 +01:00
36 changed files with 2885 additions and 84 deletions

View File

@ -27,8 +27,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 34 targetSdkVersion 34
versionCode 153 versionCode 157
versionName "2.5.4" versionName "2.5.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy" resValue "string", "app_name", "Wulkanowy"
@ -165,7 +165,7 @@ play {
track = 'production' track = 'production'
releaseStatus = ReleaseStatus.IN_PROGRESS releaseStatus = ReleaseStatus.IN_PROGRESS
userFraction = 0.99d userFraction = 0.99d
updatePriority = 1 updatePriority = 4
enabled.set(false) enabled.set(false)
} }
@ -195,7 +195,7 @@ ext {
} }
dependencies { dependencies {
implementation 'io.github.wulkanowy:sdk:2.5.4' implementation 'io.github.wulkanowy:sdk:2.5.8'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@ class WulkanowySdkFactory @Inject constructor(
) { ) {
private val eduOneMutex = Mutex() private val eduOneMutex = Mutex()
private val migrationFailedStudentIds = mutableSetOf<Long>()
private val sdk = Sdk().apply { private val sdk = Sdk().apply {
androidVersion = android.os.Build.VERSION.RELEASE androidVersion = android.os.Build.VERSION.RELEASE
@ -78,14 +79,24 @@ class WulkanowySdkFactory @Inject constructor(
private suspend fun checkEduOneAndMigrateIfNecessary(student: Student): Boolean { private suspend fun checkEduOneAndMigrateIfNecessary(student: Student): Boolean {
if (student.isEduOne != null) return student.isEduOne if (student.isEduOne != null) return student.isEduOne
if (student.id in migrationFailedStudentIds) {
Timber.i("Migration eduOne: skipping because of previous failure")
return false
}
eduOneMutex.withLock { eduOneMutex.withLock {
if (student.id in migrationFailedStudentIds) {
Timber.i("Migration eduOne: skipping because of previous failure")
return false
}
val studentFromDatabase = studentDb.loadById(student.id) val studentFromDatabase = studentDb.loadById(student.id)
if (studentFromDatabase?.isEduOne != null) { if (studentFromDatabase?.isEduOne != null) {
Timber.d("Migration eduOne: already done") Timber.i("Migration eduOne: already done")
return studentFromDatabase.isEduOne return studentFromDatabase.isEduOne
} }
Timber.d("Migration eduOne: flag missing. Running migration...") Timber.i("Migration eduOne: flag missing. Running migration...")
val initializedSdk = buildSdk( val initializedSdk = buildSdk(
student = student, student = student,
semester = null, semester = null,
@ -96,11 +107,12 @@ class WulkanowySdkFactory @Inject constructor(
.getOrNull() .getOrNull()
if (newCurrentStudent == null) { if (newCurrentStudent == null) {
Timber.d("Migration eduOne: failed, so skipping") Timber.i("Migration eduOne: failed, so skipping")
migrationFailedStudentIds.add(student.id)
return false return false
} }
Timber.d("Migration eduOne: success. New isEduOne flag: ${newCurrentStudent.isEduOne}") Timber.i("Migration eduOne: success. New isEduOne flag: ${newCurrentStudent.isEduOne}")
val studentIsEduOne = StudentIsEduOne( val studentIsEduOne = StudentIsEduOne(
id = student.id, id = student.id,

View File

@ -177,6 +177,7 @@ import javax.inject.Singleton
AutoMigration(from = 60, to = 61), AutoMigration(from = 60, to = 61),
AutoMigration(from = 61, to = 62), AutoMigration(from = 61, to = 62),
AutoMigration(from = 62, to = 63, spec = Migration63::class), AutoMigration(from = 62, to = 63, spec = Migration63::class),
AutoMigration(from = 63, to = 64),
], ],
version = AppDatabase.VERSION_SCHEMA, version = AppDatabase.VERSION_SCHEMA,
exportSchema = true exportSchema = true
@ -185,7 +186,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 63 const val VERSION_SCHEMA = 64
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(), Migration2(),

View File

@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface MobileDeviceDao : BaseDao<MobileDevice> { interface MobileDeviceDao : BaseDao<MobileDevice> {
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC") @Query("SELECT * FROM MobileDevices WHERE user_login_id = :studentId ORDER BY date DESC")
fun loadAll(userLoginId: Int): Flow<List<MobileDevice>> fun loadAll(studentId: Int): Flow<List<MobileDevice>>
} }

View File

@ -10,6 +10,6 @@ import javax.inject.Singleton
@Singleton @Singleton
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> { interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
@Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :userLoginId ORDER BY date DESC") @Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :studentId ORDER BY date DESC")
fun loadAll(userLoginId: Int): Flow<List<SchoolAnnouncement>> fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
} }

View File

@ -14,6 +14,6 @@ interface SemesterDao : BaseDao<Semester> {
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertSemesters(items: List<Semester>): List<Long> suspend fun insertSemesters(items: List<Semester>): List<Long>
@Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId") @Query("SELECT * FROM Semesters WHERE (student_id = :studentId AND class_id = :classId) OR (student_id = :studentId AND class_id = 0)")
suspend fun loadAll(studentId: Int, classId: Int): List<Semester> suspend fun loadAll(studentId: Int, classId: Int): List<Semester>
} }

View File

@ -47,11 +47,11 @@ abstract class StudentDao {
abstract suspend fun loadAll(): List<Student> abstract suspend fun loadAll(): List<Student>
@Transaction @Transaction
@Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id") @Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0)")
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>> abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
@Transaction @Transaction
@Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id WHERE Students.id = :id") @Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0) WHERE Students.id = :id")
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>> abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
@Query("UPDATE Students SET is_current = 1 WHERE id = :id") @Query("UPDATE Students SET is_current = 1 WHERE id = :id")

View File

@ -33,7 +33,13 @@ data class GradeSummary(
@ColumnInfo(name = "points_sum") @ColumnInfo(name = "points_sum")
val pointsSum: String, val pointsSum: String,
val average: Double @ColumnInfo(name = "points_sum_all_year")
val pointsSumAllYear: String?,
val average: Double,
@ColumnInfo(name = "average_all_year")
val averageAllYear: Double? = null,
) { ) {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0 var id: Long = 0

View File

@ -9,8 +9,8 @@ import java.time.Instant
@Entity(tableName = "MobileDevices") @Entity(tableName = "MobileDevices")
data class MobileDevice( data class MobileDevice(
@ColumnInfo(name = "user_login_id") @ColumnInfo(name = "user_login_id") // todo: change column name
val userLoginId: Int, val studentId: Int,
@ColumnInfo(name = "device_id") @ColumnInfo(name = "device_id")
val deviceId: Int, val deviceId: Int,

View File

@ -9,8 +9,8 @@ import java.time.LocalDate
@Entity(tableName = "SchoolAnnouncements") @Entity(tableName = "SchoolAnnouncements")
data class SchoolAnnouncement( data class SchoolAnnouncement(
@ColumnInfo(name = "user_login_id") @ColumnInfo(name = "user_login_id") // todo: change column name
val userLoginId: Int, val studentId: Int,
val date: LocalDate, val date: LocalDate,

View File

@ -49,6 +49,7 @@ data class Student(
@ColumnInfo(name = "student_id") @ColumnInfo(name = "student_id")
val studentId: Int, val studentId: Int,
@Deprecated("not available in VULCAN anymore")
@ColumnInfo(name = "user_login_id") @ColumnInfo(name = "user_login_id")
val userLoginId: Int, val userLoginId: Int,

View File

@ -8,7 +8,7 @@ import io.github.wulkanowy.sdk.pojo.LastAnnouncement as SdkLastAnnouncement
@JvmName("mapDirectorInformationToEntities") @JvmName("mapDirectorInformationToEntities")
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map { fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
SchoolAnnouncement( SchoolAnnouncement(
userLoginId = student.userLoginId, studentId = student.studentId,
date = it.date, date = it.date,
subject = it.subject, subject = it.subject,
content = it.content, content = it.content,
@ -19,7 +19,7 @@ fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
@JvmName("mapLastAnnouncementsToEntities") @JvmName("mapLastAnnouncementsToEntities")
fun List<SdkLastAnnouncement>.mapToEntities(student: Student) = map { fun List<SdkLastAnnouncement>.mapToEntities(student: Student) = map {
SchoolAnnouncement( SchoolAnnouncement(
userLoginId = student.userLoginId, studentId = student.studentId,
date = it.date, date = it.date,
subject = it.subject, subject = it.subject,
content = it.content, content = it.content,

View File

@ -37,9 +37,11 @@ fun List<SdkGradeSummary>.mapToEntities(semester: Semester) = map {
predictedGrade = it.predicted, predictedGrade = it.predicted,
finalGrade = it.final, finalGrade = it.final,
pointsSum = it.pointsSum, pointsSum = it.pointsSum,
pointsSumAllYear = it.pointsSumAllYear,
proposedPoints = it.proposedPoints, proposedPoints = it.proposedPoints,
finalPoints = it.finalPoints, finalPoints = it.finalPoints,
average = it.average average = it.average,
averageAllYear = it.averageAllYear,
) )
} }

View File

@ -8,7 +8,7 @@ import io.github.wulkanowy.sdk.pojo.Token as SdkToken
fun List<SdkDevice>.mapToEntities(student: Student) = map { fun List<SdkDevice>.mapToEntities(student: Student) = map {
MobileDevice( MobileDevice(
userLoginId = student.userLoginId, studentId = student.studentId,
date = it.createDate.toInstant(), date = it.createDate.toInstant(),
deviceId = it.id, deviceId = it.id,
name = it.name name = it.name

View File

@ -38,7 +38,7 @@ class MobileDeviceRepository @Inject constructor(
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
it.isEmpty() || forceRefresh || isExpired it.isEmpty() || forceRefresh || isExpired
}, },
query = { mobileDb.loadAll(student.userLoginId) }, query = { mobileDb.loadAll(student.studentId) },
fetch = { fetch = {
wulkanowySdkFactory.create(student, semester) wulkanowySdkFactory.create(student, semester)
.getRegisteredDevices() .getRegisteredDevices()

View File

@ -37,7 +37,7 @@ class SchoolAnnouncementRepository @Inject constructor(
it.isEmpty() || forceRefresh || isExpired it.isEmpty() || forceRefresh || isExpired
}, },
query = { query = {
schoolAnnouncementDb.loadAll(student.userLoginId) schoolAnnouncementDb.loadAll(student.studentId)
}, },
fetch = { fetch = {
val sdk = wulkanowySdkFactory.create(student) val sdk = wulkanowySdkFactory.create(student)
@ -57,7 +57,7 @@ class SchoolAnnouncementRepository @Inject constructor(
) )
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> { fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
return schoolAnnouncementDb.loadAll(student.userLoginId) return schoolAnnouncementDb.loadAll(student.studentId)
} }
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) = suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) =

View File

@ -64,7 +64,10 @@ class SemesterRepository @Inject constructor(
.getSemesters() .getSemesters()
.mapToEntities(student.studentId) .mapToEntities(student.studentId)
if (new.isEmpty()) return Timber.i("Empty semester list!") if (new.isEmpty()) {
Timber.i("Empty semester list from SDK!")
return
}
val old = semesterDb.loadAll(student.studentId, student.classId) val old = semesterDb.loadAll(student.studentId, student.classId)
semesterDb.removeOldAndSaveNew( semesterDb.removeOldAndSaveNew(

View File

@ -26,5 +26,7 @@ private fun generateSummary(subject: String, predicted: String, final: String) =
proposedPoints = "", proposedPoints = "",
finalPoints = "", finalPoints = "",
pointsSum = "", pointsSum = "",
average = .0 average = .0,
pointsSumAllYear = null,
averageAllYear = null,
) )

View File

@ -19,6 +19,6 @@ val debugSchoolAnnouncementItems = listOf(
private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement( private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement(
subject = subject, subject = subject,
content = content, content = content,
userLoginId = 0, studentId = 0,
date = LocalDate.now() date = LocalDate.now()
) )

View File

@ -266,7 +266,9 @@ class GradeAverageProvider @Inject constructor(
proposedPoints = "", proposedPoints = "",
finalPoints = "", finalPoints = "",
pointsSum = "", pointsSum = "",
average = .0 pointsSumAllYear = null,
average = .0,
averageAllYear = null,
) )
} }
@ -294,13 +296,15 @@ class GradeAverageProvider @Inject constructor(
proposedPoints = "", proposedPoints = "",
finalPoints = "", finalPoints = "",
pointsSum = "", pointsSum = "",
pointsSumAllYear = null,
average = when { average = when {
calcAverage -> details calcAverage -> details
.updateModifiers(student, params) .updateModifiers(student, params)
.calcAverage(isOptionalArithmeticAverage = params.isOptionalArithmeticAverage) .calcAverage(isOptionalArithmeticAverage = params.isOptionalArithmeticAverage)
else -> .0 else -> .0
} },
averageAllYear = null,
) )
} }
} }

View File

@ -96,9 +96,11 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
ViewType.HEADER.id -> HeaderViewHolder( ViewType.HEADER.id -> HeaderViewHolder(
HeaderGradeDetailsBinding.inflate(inflater, parent, false) HeaderGradeDetailsBinding.inflate(inflater, parent, false)
) )
ViewType.ITEM.id -> ItemViewHolder( ViewType.ITEM.id -> ItemViewHolder(
ItemGradeDetailsBinding.inflate(inflater, parent, false) ItemGradeDetailsBinding.inflate(inflater, parent, false)
) )
else -> throw IllegalStateException() else -> throw IllegalStateException()
} }
} }
@ -110,6 +112,7 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
header = items[position].value as GradeDetailsHeader, header = items[position].value as GradeDetailsHeader,
position = position position = position
) )
is ItemViewHolder -> bindItemViewHolder( is ItemViewHolder -> bindItemViewHolder(
holder = holder, holder = holder,
grade = items[position].value as Grade grade = items[position].value as Grade
@ -133,6 +136,10 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
maxLines = if (expandedPositions[headerPosition]) 2 else 1 maxLines = if (expandedPositions[headerPosition]) 2 else 1
} }
gradeHeaderAverage.text = formatAverage(header.average, root.context.resources) gradeHeaderAverage.text = formatAverage(header.average, root.context.resources)
with(gradeHeaderAverageAllYear) {
isVisible = header.averageAllYear != null && header.averageAllYear != .0
text = formatAverageAllYear(header.averageAllYear, root.context.resources)
}
gradeHeaderPointsSum.text = gradeHeaderPointsSum.text =
context.getString(R.string.grade_points_sum, header.pointsSum) context.getString(R.string.grade_points_sum, header.pointsSum)
gradeHeaderPointsSum.isVisible = !header.pointsSum.isNullOrEmpty() gradeHeaderPointsSum.isVisible = !header.pointsSum.isNullOrEmpty()
@ -233,6 +240,13 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
resources.getString(R.string.grade_average, average) resources.getString(R.string.grade_average, average)
} }
private fun formatAverageAllYear(average: Double?, resources: Resources) =
if (average == null || average == .0) {
resources.getString(R.string.grade_no_average)
} else {
resources.getString(R.string.grade_average_year, average)
}
private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) : private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)

View File

@ -13,6 +13,7 @@ data class GradeDetailsItem(
data class GradeDetailsHeader( data class GradeDetailsHeader(
val subject: String, val subject: String,
val average: Double?, val average: Double?,
val averageAllYear: Double?,
val pointsSum: String?, val pointsSum: String?,
val grades: List<GradeDetailsItem> val grades: List<GradeDetailsItem>
) { ) {

View File

@ -226,8 +226,9 @@ class GradeDetailsPresenter @Inject constructor(
GradeDetailsHeader( GradeDetailsHeader(
subject = gradeSubject.subject, subject = gradeSubject.subject,
average = gradeSubject.average, average = gradeSubject.average,
averageAllYear = gradeSubject.summary.averageAllYear,
pointsSum = gradeSubject.points, pointsSum = gradeSubject.points,
grades = subItems grades = subItems,
).apply { ).apply {
newGrades = gradeSubject.grades.filter { grade -> !grade.isRead }.size newGrades = gradeSubject.grades.filter { grade -> !grade.isRead }.size
}, ViewType.HEADER }, ViewType.HEADER

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.grade.summary
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R import io.github.wulkanowy.R
@ -65,37 +66,55 @@ class GradeSummaryAdapter @Inject constructor(
val gradeSummaries = items val gradeSummaries = items
.filter { it.gradeDescriptive == null } .filter { it.gradeDescriptive == null }
.map { it.gradeSummary } .map { it.gradeSummary }
val isSecondSemester = items.any { item ->
item.gradeSummary.let { it.averageAllYear != null && it.averageAllYear != .0 }
}
val context = binding.root.context val context = binding.root.context
val finalItemsCount = gradeSummaries.count { isGradeValid(it.finalGrade) } val finalItemsCount = gradeSummaries.count { isGradeValid(it.finalGrade) }
val calculatedItemsCount = gradeSummaries.count { value -> value.average != 0.0 } val calculatedSemesterItemsCount = gradeSummaries.count { value -> value.average != 0.0 }
val calculatedAnnualItemsCount =
gradeSummaries.count { value -> value.averageAllYear != 0.0 }
val allItemsCount = gradeSummaries.count { !it.subject.equals("zachowanie", true) } val allItemsCount = gradeSummaries.count { !it.subject.equals("zachowanie", true) }
val finalAverage = gradeSummaries.calcFinalAverage( val finalAverage = gradeSummaries.calcFinalAverage(
preferencesRepository.gradePlusModifier, plusModifier = preferencesRepository.gradePlusModifier,
preferencesRepository.gradeMinusModifier minusModifier = preferencesRepository.gradeMinusModifier,
) )
val calculatedAverage = gradeSummaries.filter { value -> value.average != 0.0 } val calculatedSemesterAverage = gradeSummaries.filter { value -> value.average != 0.0 }
.map { values -> values.average } .map { values -> values.average }
.reversed() // fix average precision .reversed() // fix average precision
.average() .average()
.let { if (it.isNaN()) 0.0 else it } .let { if (it.isNaN()) 0.0 else it }
val calculatedAnnualAverage = gradeSummaries.filter { value -> value.averageAllYear != 0.0 }
.mapNotNull { values -> values.averageAllYear }
.reversed() // fix average precision
.average()
.let { if (it.isNaN()) 0.0 else it }
with(binding) { with(binding) {
gradeSummaryScrollableHeaderCalculated.text = formatAverage(calculatedSemesterAverage)
gradeSummaryScrollableHeaderCalculatedAnnual.text =
formatAverage(calculatedAnnualAverage)
gradeSummaryScrollableHeaderFinal.text = formatAverage(finalAverage) gradeSummaryScrollableHeaderFinal.text = formatAverage(finalAverage)
gradeSummaryScrollableHeaderCalculated.text = formatAverage(calculatedAverage) gradeSummaryScrollableHeaderFinalSubjectCount.text = context.getString(
gradeSummaryScrollableHeaderFinalSubjectCount.text =
context.getString(
R.string.grade_summary_from_subjects,
finalItemsCount,
allItemsCount
)
gradeSummaryScrollableHeaderCalculatedSubjectCount.text = context.getString(
R.string.grade_summary_from_subjects, R.string.grade_summary_from_subjects,
calculatedItemsCount, finalItemsCount,
allItemsCount allItemsCount
) )
gradeSummaryScrollableHeaderCalculatedSubjectCount.text = context.getString(
R.string.grade_summary_from_subjects,
calculatedSemesterItemsCount,
allItemsCount
)
gradeSummaryScrollableHeaderCalculatedSubjectCountAnnual.text = context.getString(
R.string.grade_summary_from_subjects,
calculatedAnnualItemsCount,
allItemsCount
)
gradeSummaryScrollableHeaderCalculatedAnnualContainer.isVisible = isSecondSemester
gradeSummaryCalculatedAverageHelp.setOnClickListener { onCalculatedHelpClickListener() } gradeSummaryCalculatedAverageHelp.setOnClickListener { onCalculatedHelpClickListener() }
gradeSummaryCalculatedAverageHelpAnnual.setOnClickListener { onCalculatedHelpClickListener() }
gradeSummaryFinalAverageHelp.setOnClickListener { onFinalHelpClickListener() } gradeSummaryFinalAverageHelp.setOnClickListener { onFinalHelpClickListener() }
} }
} }
@ -107,7 +126,12 @@ class GradeSummaryAdapter @Inject constructor(
with(binding) { with(binding) {
gradeSummaryItemTitle.text = gradeSummary.subject gradeSummaryItemTitle.text = gradeSummary.subject
gradeSummaryItemPoints.text = gradeSummary.pointsSum gradeSummaryItemPoints.text = gradeSummary.pointsSum
gradeSummaryItemAverage.text = formatAverage(gradeSummary.average, "") gradeSummaryItemAverage.text = formatAverage(gradeSummary.average, "")
gradeSummaryItemAverageAllYear.text = gradeSummary.averageAllYear?.let {
formatAverage(it, "")
}
gradeSummaryItemPredicted.text = gradeSummaryItemPredicted.text =
"${gradeSummary.predictedGrade} ${gradeSummary.proposedPoints}".trim() "${gradeSummary.predictedGrade} ${gradeSummary.proposedPoints}".trim()
gradeSummaryItemFinal.text = gradeSummaryItemFinal.text =
@ -116,6 +140,12 @@ class GradeSummaryAdapter @Inject constructor(
root.context.getString(R.string.all_no_data) root.context.getString(R.string.all_no_data)
} }
gradeSummaryItemAverageContainer.isVisible = gradeSummary.average != .0
gradeSummaryItemAverageDivider.isVisible = gradeSummaryItemAverageContainer.isVisible
gradeSummaryItemAverageAllYearContainer.isGone =
gradeSummary.averageAllYear == null || gradeSummary.averageAllYear == .0
gradeSummaryItemAverageAllYearDivider.isGone =
gradeSummaryItemAverageAllYearContainer.isGone
gradeSummaryItemFinalDivider.isVisible = gradeDescriptive == null gradeSummaryItemFinalDivider.isVisible = gradeDescriptive == null
gradeSummaryItemPredictedDivider.isVisible = gradeDescriptive == null gradeSummaryItemPredictedDivider.isVisible = gradeDescriptive == null
gradeSummaryItemPointsDivider.isVisible = gradeDescriptive == null gradeSummaryItemPointsDivider.isVisible = gradeDescriptive == null
@ -123,6 +153,7 @@ class GradeSummaryAdapter @Inject constructor(
gradeSummaryItemFinalContainer.isVisible = gradeDescriptive == null gradeSummaryItemFinalContainer.isVisible = gradeDescriptive == null
gradeSummaryItemDescriptiveContainer.isVisible = gradeDescriptive != null gradeSummaryItemDescriptiveContainer.isVisible = gradeDescriptive != null
gradeSummaryItemPointsContainer.isVisible = gradeSummary.pointsSum.isNotBlank() gradeSummaryItemPointsContainer.isVisible = gradeSummary.pointsSum.isNotBlank()
gradeSummaryItemPointsDivider.isVisible = gradeSummaryItemPointsContainer.isVisible
} }
} }

View File

@ -19,19 +19,23 @@ class LoginStudentSelectAdapter @Inject constructor() :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
return when (LoginStudentSelectItemType.values()[viewType]) { return when (LoginStudentSelectItemType.entries[viewType]) {
LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER -> EmptySymbolsHeaderViewHolder( LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER -> EmptySymbolsHeaderViewHolder(
ItemLoginStudentSelectEmptySymbolHeaderBinding.inflate(inflater, parent, false), ItemLoginStudentSelectEmptySymbolHeaderBinding.inflate(inflater, parent, false),
) )
LoginStudentSelectItemType.SYMBOL_HEADER -> SymbolsHeaderViewHolder( LoginStudentSelectItemType.SYMBOL_HEADER -> SymbolsHeaderViewHolder(
ItemLoginStudentSelectHeaderSymbolBinding.inflate(inflater, parent, false) ItemLoginStudentSelectHeaderSymbolBinding.inflate(inflater, parent, false)
) )
LoginStudentSelectItemType.SCHOOL_HEADER -> SchoolHeaderViewHolder( LoginStudentSelectItemType.SCHOOL_HEADER -> SchoolHeaderViewHolder(
ItemLoginStudentSelectHeaderSchoolBinding.inflate(inflater, parent, false) ItemLoginStudentSelectHeaderSchoolBinding.inflate(inflater, parent, false)
) )
LoginStudentSelectItemType.STUDENT -> StudentViewHolder( LoginStudentSelectItemType.STUDENT -> StudentViewHolder(
ItemLoginStudentSelectStudentBinding.inflate(inflater, parent, false) ItemLoginStudentSelectStudentBinding.inflate(inflater, parent, false)
) )
LoginStudentSelectItemType.HELP -> HelpViewHolder( LoginStudentSelectItemType.HELP -> HelpViewHolder(
ItemLoginStudentSelectHelpBinding.inflate(inflater, parent, false) ItemLoginStudentSelectHelpBinding.inflate(inflater, parent, false)
) )
@ -98,9 +102,11 @@ class LoginStudentSelectAdapter @Inject constructor() :
with(binding) { with(binding) {
loginStudentSelectHeaderSchoolName.text = buildString { loginStudentSelectHeaderSchoolName.text = buildString {
append(item.unit.schoolName.trim()) append(item.unit.schoolName.trim())
append(" (") if (item.unit.schoolShortName.isNotBlank()) {
append(item.unit.schoolShortName) append(" (")
append(")") append(item.unit.schoolShortName)
append(")")
}
} }
loginStudentSelectHeaderSchoolDetails.isVisible = item.unit.students.isEmpty() loginStudentSelectHeaderSchoolDetails.isVisible = item.unit.students.isEmpty()
loginStudentSelectHeaderSchoolError.text = item.unit.error?.message loginStudentSelectHeaderSchoolError.text = item.unit.error?.message
@ -170,9 +176,11 @@ class LoginStudentSelectAdapter @Inject constructor() :
oldItem is LoginStudentSelectItem.SymbolHeader && newItem is LoginStudentSelectItem.SymbolHeader -> { oldItem is LoginStudentSelectItem.SymbolHeader && newItem is LoginStudentSelectItem.SymbolHeader -> {
oldItem.symbol == newItem.symbol oldItem.symbol == newItem.symbol
} }
oldItem is LoginStudentSelectItem.Student && newItem is LoginStudentSelectItem.Student -> { oldItem is LoginStudentSelectItem.Student && newItem is LoginStudentSelectItem.Student -> {
oldItem.student == newItem.student oldItem.student == newItem.student
} }
else -> oldItem == newItem else -> oldItem == newItem
} }

View File

@ -12,6 +12,7 @@ import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.data.repositories.SchoolsRepository import io.github.wulkanowy.data.repositories.SchoolsRepository
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.sdk.scrapper.exception.StudentGraduateException
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
@ -108,8 +109,8 @@ class LoginStudentSelectPresenter @Inject constructor(
} }
private fun createItems(): List<LoginStudentSelectItem> = buildList { private fun createItems(): List<LoginStudentSelectItem> = buildList {
val notEmptySymbols = registerUser.symbols.filter { it.schools.isNotEmpty() } val notEmptySymbols = registerUser.symbols.filter { it.shouldShowOnTop() }
val emptySymbols = registerUser.symbols.filter { it.schools.isEmpty() } val emptySymbols = registerUser.symbols.filter { !it.shouldShowOnTop() }
if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.userEnteredSymbol }) { if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.userEnteredSymbol }) {
add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.userEnteredSymbol })) add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.userEnteredSymbol }))
@ -127,6 +128,10 @@ class LoginStudentSelectPresenter @Inject constructor(
add(helpItem) add(helpItem)
} }
private fun RegisterSymbol.shouldShowOnTop(): Boolean {
return schools.isNotEmpty() || error is StudentGraduateException
}
private fun createNotEmptySymbolItems( private fun createNotEmptySymbolItems(
notEmptySymbols: List<RegisterSymbol>, notEmptySymbols: List<RegisterSymbol>,
students: List<StudentWithSemesters>, students: List<StudentWithSemesters>,

View File

@ -23,7 +23,7 @@ fun getRefreshKey(name: String, semester: Semester): String {
} }
fun getRefreshKey(name: String, student: Student): String { fun getRefreshKey(name: String, student: Student): String {
return "${name}_${student.userLoginId}" return "${name}_${student.studentId}"
} }
fun getRefreshKey(name: String, mailbox: Mailbox?, folder: MessageFolder): String { fun getRefreshKey(name: String, mailbox: Mailbox?, folder: MessageFolder): String {

View File

@ -1,5 +1,5 @@
Wersja 2.5.4 Wersja 2.5.8
naprawiliśmy kolejnych kilka błędów związanych z obsługą dziennika eduOne (tak, nie umiemy za jednym razem) obeszliśmy próby blokowania Wulkanowego przez firmę VULCAN, o czymś pewnie zapomnieliśmy, ale nie miejcie nam tego za złe
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -45,13 +45,30 @@
android:textColor="?android:textColorSecondary" android:textColor="?android:textColorSecondary"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/gradeHeaderPointsSum" app:layout_constraintEnd_toStartOf="@id/gradeHeaderAverageAllYear"
app:layout_constraintHorizontal_bias="0" app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="@id/gradeHeaderSubject" app:layout_constraintStart_toStartOf="@id/gradeHeaderSubject"
app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject" app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject"
tools:text="Average: 6,00" /> tools:text="Average: 6,00" />
<TextView
android:id="@+id/gradeHeaderAverageAllYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="@id/gradeHeaderPointsSum"
app:layout_constraintStart_toEndOf="@+id/gradeHeaderAverage"
app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject"
tools:text="Roczna: 5,00"
tools:visibility="gone" />
<TextView <TextView
android:id="@+id/gradeHeaderPointsSum" android:id="@+id/gradeHeaderPointsSum"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -64,7 +81,7 @@
android:textSize="12sp" android:textSize="12sp"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="@id/gradeHeaderNumber" app:layout_constraintEnd_toStartOf="@id/gradeHeaderNumber"
app:layout_constraintStart_toEndOf="@+id/gradeHeaderAverage" app:layout_constraintStart_toEndOf="@+id/gradeHeaderAverageAllYear"
app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject" app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject"
tools:text="Points: 123/200 (61,5%)" /> tools:text="Points: 123/200 (61,5%)" />

View File

@ -20,20 +20,80 @@
android:id="@+id/gradeSummaryItemTitle" android:id="@+id/gradeSummaryItemTitle"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="40dp"
android:layout_weight="1" android:layout_weight="1"
android:textSize="17sp" android:textSize="17sp"
tools:text="@tools:sample/lorem" /> tools:text="@tools:sample/lorem" />
</LinearLayout>
<LinearLayout
android:id="@+id/gradeSummaryItemAverageContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="35dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:layout_weight="1"
android:text="@string/grade_summary_average_semester"
android:textSize="14sp" />
<TextView <TextView
android:id="@+id/gradeSummaryItemAverage" android:id="@+id/gradeSummaryItemAverage"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:layout_marginEnd="25dp"
android:gravity="end" android:gravity="end"
android:textSize="12sp" android:textSize="12sp"
tools:text="4.74" /> tools:text="2,50" />
</LinearLayout> </LinearLayout>
<View
android:id="@+id/gradeSummaryItemAverageDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@drawable/ic_all_divider" />
<LinearLayout
android:id="@+id/gradeSummaryItemAverageAllYearContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="35dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:layout_weight="1"
android:text="@string/grade_summary_average_year"
android:textSize="14sp" />
<TextView
android:id="@+id/gradeSummaryItemAverageAllYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:layout_marginEnd="25dp"
android:gravity="end"
android:textSize="12sp"
tools:text="4,50" />
</LinearLayout>
<View
android:id="@+id/gradeSummaryItemAverageAllYearDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@drawable/ic_all_divider" />
<LinearLayout <LinearLayout
android:id="@+id/gradeSummaryItemPointsContainer" android:id="@+id/gradeSummaryItemPointsContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -63,9 +123,9 @@
</LinearLayout> </LinearLayout>
<View <View
android:id="@+id/gradeSummaryItemPointsDivider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:id="@+id/gradeSummaryItemPointsDivider"
android:background="@drawable/ic_all_divider" /> android:background="@drawable/ic_all_divider" />
<LinearLayout <LinearLayout
@ -97,8 +157,8 @@
</LinearLayout> </LinearLayout>
<View <View
android:layout_width="match_parent"
android:id="@+id/gradeSummaryItemPredictedDivider" android:id="@+id/gradeSummaryItemPredictedDivider"
android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:background="@drawable/ic_all_divider" /> android:background="@drawable/ic_all_divider" />
@ -131,9 +191,9 @@
</LinearLayout> </LinearLayout>
<View <View
android:id="@+id/gradeSummaryItemFinalDivider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:id="@+id/gradeSummaryItemFinalDivider"
android:background="@drawable/ic_all_divider" /> android:background="@drawable/ic_all_divider" />
<LinearLayout <LinearLayout

View File

@ -10,10 +10,11 @@
tools:context=".ui.modules.grade.summary.GradeSummaryAdapter"> tools:context=".ui.modules.grade.summary.GradeSummaryAdapter">
<LinearLayout <LinearLayout
android:layout_width="150dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"
android:layout_marginEnd="15dp" android:layout_marginEnd="15dp"
android:layout_weight="1"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
@ -21,9 +22,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:minLines="2" android:minLines="2"
android:textStyle="bold"
android:text="@string/grade_summary_calculated_average" android:text="@string/grade_summary_calculated_average"
android:textSize="16sp" /> android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -61,9 +62,64 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="150dp" android:id="@+id/gradeSummaryScrollableHeaderCalculatedAnnualContainer"
android:layout_height="wrap_content" android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"
android:layout_marginEnd="15dp"
android:layout_weight="1"
android:orientation="vertical"
tools:visibility="visible">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:minLines="2"
android:text="@string/grade_summary_calculated_average_annual"
android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/gradeSummaryScrollableHeaderCalculatedAnnual"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="21sp"
tools:text="6,00" />
<ImageButton
android:id="@+id/gradeSummaryCalculatedAverageHelpAnnual"
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/gradeSummaryScrollableHeaderCalculatedSubjectCountAnnual"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center_horizontal"
android:textSize="13sp"
tools:text="from 8 subjects" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="20dp"
android:layout_weight="1"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
@ -71,9 +127,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:minLines="2" android:minLines="2"
android:textStyle="bold"
android:text="@string/grade_summary_final_average" android:text="@string/grade_summary_final_average"
android:textSize="16sp" /> android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -103,8 +159,8 @@
<TextView <TextView
android:id="@+id/gradeSummaryScrollableHeaderFinalSubjectCount" android:id="@+id/gradeSummaryScrollableHeaderFinalSubjectCount"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_marginTop="5dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:textSize="13sp" android:textSize="13sp"
tools:text="from 5 subjects" /> tools:text="from 5 subjects" />

View File

@ -113,13 +113,17 @@
<string name="grade_comment">Komentarz</string> <string name="grade_comment">Komentarz</string>
<string name="grade_number_new_items">Ilość nowych ocen: %1$d</string> <string name="grade_number_new_items">Ilość nowych ocen: %1$d</string>
<string name="grade_average">Średnia: %1$.2f</string> <string name="grade_average">Średnia: %1$.2f</string>
<string name="grade_average_year">Roczna: %1$.2f</string>
<string name="grade_points_sum">Punkty: %s</string> <string name="grade_points_sum">Punkty: %s</string>
<string name="grade_no_average">Brak średniej</string> <string name="grade_no_average">Brak średniej</string>
<string name="grade_summary_average_semester">Średnia semestralna</string>
<string name="grade_summary_average_year">Średnia roczna</string>
<string name="grade_summary_points">Suma punktów</string> <string name="grade_summary_points">Suma punktów</string>
<string name="grade_summary_final_grade">Ocena końcowa</string> <string name="grade_summary_final_grade">Ocena końcowa</string>
<string name="grade_summary_predicted_grade">Przewidywana ocena</string> <string name="grade_summary_predicted_grade">Przewidywana ocena</string>
<string name="grade_summary_descriptive">Ocena opisowa</string> <string name="grade_summary_descriptive">Ocena opisowa</string>
<string name="grade_summary_calculated_average">Obliczona średnia</string> <string name="grade_summary_calculated_average">Obliczona średnia semestralna</string>
<string name="grade_summary_calculated_average_annual">Obliczona średnia roczna</string>
<string name="grade_summary_calculated_average_help_dialog_title">Jak działa 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 jest średnią arytmetyczną obliczoną 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. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\n<b>Średnia ocen tylko z wybranego semestru</b>:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia ze średnich z obu semestrów</b>:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia wszystkich ocen z całego roku:</b>\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej z zsumowanych średnich</string> <string name="grade_summary_calculated_average_help_dialog_message">Obliczona średnia jest średnią arytmetyczną obliczoną 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. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\n<b>Średnia ocen tylko z wybranego semestru</b>:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia ze średnich z obu semestrów</b>:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia wszystkich ocen z całego roku:</b>\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej z zsumowanych średnich</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_title">Jak działa końcowa średnia?</string>

View File

@ -126,13 +126,17 @@
<string name="grade_comment">Comment</string> <string name="grade_comment">Comment</string>
<string name="grade_number_new_items">Number of new ratings: %1$d</string> <string name="grade_number_new_items">Number of new ratings: %1$d</string>
<string name="grade_average">Average: %1$.2f</string> <string name="grade_average">Average: %1$.2f</string>
<string name="grade_average_year">Annual: %1$.2f</string>
<string name="grade_points_sum">Points: %s</string> <string name="grade_points_sum">Points: %s</string>
<string name="grade_no_average">No average</string> <string name="grade_no_average">No average</string>
<string name="grade_summary_average_semester">Semester average</string>
<string name="grade_summary_average_year">Annual average</string>
<string name="grade_summary_points">Total points</string> <string name="grade_summary_points">Total points</string>
<string name="grade_summary_final_grade">Final grade</string> <string name="grade_summary_final_grade">Final grade</string>
<string name="grade_summary_predicted_grade">Predicted grade</string> <string name="grade_summary_predicted_grade">Predicted grade</string>
<string name="grade_summary_descriptive">Descriptive 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">Calculated semester average</string>
<string name="grade_summary_calculated_average_annual">Calculated annual 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_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> <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>
<string name="grade_summary_final_average_help_dialog_title">How does the Final Average work?</string> <string name="grade_summary_final_average_help_dialog_title">How does the Final Average work?</string>

View File

@ -1679,7 +1679,9 @@ class GradeAverageProviderTest {
finalPoints = "", finalPoints = "",
finalGrade = "", finalGrade = "",
predictedGrade = "", predictedGrade = "",
position = 0 position = 0,
pointsSumAllYear = null,
averageAllYear = null,
) )
} }
} }

View File

@ -23,13 +23,15 @@ class GradeExtensionTest {
@Test @Test
fun calcWeightedAverage() { fun calcWeightedAverage() {
assertEquals(3.47, listOf( assertEquals(
createGrade(5.0, 6.0, 0.33), 3.47, listOf(
createGrade(5.0, 5.0, -0.33), createGrade(5.0, 6.0, 0.33),
createGrade(4.0, 1.0, 0.0), createGrade(5.0, 5.0, -0.33),
createGrade(1.0, 9.0, 0.5), createGrade(4.0, 1.0, 0.0),
createGrade(0.0, .0, 0.0) createGrade(1.0, 9.0, 0.5),
).calcAverage(false), 0.005) createGrade(0.0, .0, 0.0)
).calcAverage(false), 0.005
)
} }
@Test @Test
@ -86,7 +88,11 @@ class GradeExtensionTest {
assertEquals(-.25, createGrade(5.0, .0, -.33).changeModifier(.0, .25).modifier, .0) assertEquals(-.25, createGrade(5.0, .0, -.33).changeModifier(.0, .25).modifier, .0)
} }
private fun createGrade(value: Double, weightValue: Double = .0, modifier: Double = 0.25): Grade { private fun createGrade(
value: Double,
weightValue: Double = .0,
modifier: Double = 0.25
): Grade {
return Grade( return Grade(
semesterId = 1, semesterId = 1,
studentId = 1, studentId = 1,
@ -116,7 +122,9 @@ class GradeExtensionTest {
proposedPoints = "", proposedPoints = "",
finalPoints = "", finalPoints = "",
pointsSum = "", pointsSum = "",
average = .0 average = .0,
pointsSumAllYear = null,
averageAllYear = null,
) )
} }
} }