[UI/Grades] Update views for university grades

This commit is contained in:
Kuba Szczodrzyński 2025-01-31 23:10:07 +01:00
parent 6de7ee9cee
commit 30a77f1a99
No known key found for this signature in database
GPG Key ID: 43037AC62A600562
9 changed files with 209 additions and 22 deletions

View File

@ -153,7 +153,9 @@ class UsosApiExamReports(
if (sessionNumber > 1) {
val origId = examId * 10L + sessionNumber - 1
val grades = data.gradeList.filter { it.id == origId }
grades.firstOrNull()?.parentId = gradeObject.id
val improvedGrade = grades.firstOrNull()
improvedGrade?.parentId = gradeObject.id
improvedGrade?.weight = 0.0f
gradeObject.isImprovement = true
}

View File

@ -134,6 +134,7 @@ class GradesAdapter(
if (model.state == STATE_CLOSED) {
val subItems = when {
model is GradesSubject && manager.isUniversity -> listOf()
model is GradesSemester && model.grades.isEmpty() ->
listOf(GradesEmpty())
model is GradesSemester && manager.hideImproved ->
@ -147,10 +148,12 @@ class GradesAdapter(
if (notifyAdapter) notifyItemInserted(position)
}
position++
model.state = STATE_OPENED
if (subItems.isNotEmpty()) {
position++
items.addAll(position, subItems.filterNotNull())
if (notifyAdapter) notifyItemRangeInserted(position, subItems.size)
}
if (model is GradesSubject) {
// auto expand first semester

View File

@ -258,8 +258,40 @@ class GradesListFragment : Fragment(), CoroutineScope {
subject.lastAddedDate = max(subject.lastAddedDate, grade.addedDate)
}
when (manager.orderBy) {
GradesManager.ORDER_BY_DATE_DESC -> items.sortByDescending { it.lastAddedDate }
GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate }
}
val stats = GradesStats()
if (manager.isUniversity) {
val semesterSum = mutableListOf<Float>()
val semesterCount = mutableListOf<Float>()
val totalSum = mutableListOf<Float>()
val totalCount = mutableListOf<Float>()
val ectsPoints = mutableMapOf<Long, Float>()
for (grade in grades) {
if (grade.value == 0.0f || grade.weight == 0.0f)
continue
totalSum.add(grade.value * grade.weight)
totalCount.add(grade.weight)
if (grade.value < 3.0)
// exam not passed, reset points for this subject
ectsPoints[grade.subjectId] = 0.0f
else if (grade.subjectId !in ectsPoints)
// no points for this subject, simply assign
ectsPoints[grade.subjectId] = grade.weight
semesterSum.add(grade.value * grade.weight)
semesterCount.add(grade.weight)
}
stats.universitySem = semesterSum.sum() / semesterCount.sum()
stats.universityTotal = totalSum.sum() / totalCount.sum()
stats.universityEcts = ectsPoints.values.sum()
return (items + stats).toMutableList()
}
val sem1Expected = mutableListOf<Float>()
val sem2Expected = mutableListOf<Float>()
val yearlyExpected = mutableListOf<Float>()
@ -330,11 +362,6 @@ class GradesListFragment : Fragment(), CoroutineScope {
stats.pointSem2 = sem2Point.averageOrNull()?.toFloat() ?: 0f
stats.pointYearly = yearlyPoint.averageOrNull()?.toFloat() ?: 0f
when (manager.orderBy) {
GradesManager.ORDER_BY_DATE_DESC -> items.sortByDescending { it.lastAddedDate }
GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate }
}
return (items + stats).toMutableList()
}

View File

@ -22,4 +22,8 @@ class GradesStats {
var pointSem1 = 0f
var pointSem2 = 0f
var pointYearly = 0f
var universitySem = 0f
var universityTotal = 0f
var universityEcts = 0f
}

View File

@ -31,9 +31,35 @@ class StatsViewHolder(
override fun onBind(activity: AppCompatActivity, app: App, item: GradesStats, position: Int, adapter: GradesAdapter) {
val manager = app.gradesManager
val isUniversity = manager.isUniversity
val showAverages = mutableListOf<Int>()
val showPoint = mutableListOf<Int>()
b.universityTitle.isVisible = isUniversity
b.universityLayout.isVisible = isUniversity
b.universityDivider.isVisible = isUniversity
if (isUniversity) {
val format = DecimalFormat("#.00")
b.normalTitle.isVisible = false
b.normalLayout.isVisible = false
b.normalDivider.isVisible = false
b.helpButton.isVisible = false
b.pointTitle.isVisible = false
b.pointLayout.isVisible = false
b.pointDivider.isVisible = false
b.noData.isVisible = false
b.disclaimer.isVisible = true
b.customValueDivider.isVisible = false
b.customValueLayout.isVisible = false
b.universitySemester.text = format.format(item.universitySem)
b.universityTotal.text = format.format(item.universityTotal)
b.universityEcts.text = format.format(item.universityEcts)
return
}
getSemesterString(app, item.normalSem1, item.normalSem1Proposed, item.normalSem1Final, item.sem1NotAllFinal).let { (average, notice) ->
b.normalSemester1Layout.isVisible = average != null
b.normalSemester1Notice.isVisible = notice != null

View File

@ -62,9 +62,24 @@ class SubjectViewHolder(
val firstSemester = item.semesters.firstOrNull() ?: return
b.yearSummary.text = manager.getYearSummaryString(app, item.semesters.map { it.grades.size }.sum(), item.averages)
if (manager.isUniversity) {
val ectsPoints = item.semesters.firstOrNull()?.grades?.maxOf { it.weight }
b.yearSummary.text = if (ectsPoints != null)
contextWrapper.getString(
R.string.grades_ects_points_format,
ectsPoints
)
else
null
} else {
b.yearSummary.text = manager.getYearSummaryString(
app,
item.semesters.map { it.grades.size }.sum(),
item.averages
)
}
if (firstSemester.number != item.semester) {
if (firstSemester.number != item.semester && !manager.isUniversity) {
b.gradesContainer.addView(TextView(contextWrapper).apply {
setTextColor(android.R.attr.textColorSecondary.resolveAttr(context))
setText(R.string.grades_preview_other_semester, firstSemester.number)
@ -92,6 +107,7 @@ class SubjectViewHolder(
))
}
if (!manager.isUniversity) {
b.previewContainer.addView(TextView(contextWrapper).apply {
setTextColor(android.R.attr.textColorSecondary.resolveAttr(context))
text = manager.getAverageString(app, firstSemester.averages, nameSemester = true, showSemester = firstSemester.number)
@ -102,6 +118,7 @@ class SubjectViewHolder(
maxLines = 1
ellipsize = TextUtils.TruncateAt.END
})
}
// add the topmost semester's grades to preview container (collapsed)
firstSemester.proposedGrade?.let {
@ -139,7 +156,7 @@ class SubjectViewHolder(
}
// if showing semester 2, add yearly grades to preview container (collapsed)
if (firstSemester.number == item.semester) {
if (firstSemester.number == item.semester && !manager.isUniversity) {
b.previewContainer.addView(TextView(contextWrapper).apply {
text = manager.getAverageString(app, item.averages, nameSemester = true)
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {

View File

@ -70,6 +70,8 @@ class GradesManager(val app: App) : CoroutineScope {
get() = app.profile.config.grades.hideImproved
val averageWithoutWeight
get() = app.profile.config.grades.averageWithoutWeight
val isUniversity
get() = app.profile.loginStoreType.schoolType == SchoolType.UNIVERSITY
fun getOrderByString() = when (orderBy) {

View File

@ -283,6 +283,107 @@
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<TextView
android:id="@+id/universityTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:text="@string/grades_stats_university"
android:textAppearance="@style/NavView.TextView.Subtitle" />
<LinearLayout
android:id="@+id/universityLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="8dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_this_semester" />
<TextView
android:id="@+id/universitySemester"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="4,56" />
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_all_grades" />
<TextView
android:id="@+id/universityTotal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="4,67" />
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:background="@drawable/divider"
android:visibility="gone" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_ects_points" />
<TextView
android:id="@+id/universityEcts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="120" />
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/universityDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<TextView
android:id="@+id/disclaimer"
android:layout_width="wrap_content"

View File

@ -448,6 +448,7 @@
<string name="grades_config_minus_value">Własna wartość minusa</string>
<string name="grades_config_plus_value">Własna wartość plusa</string>
<string name="grades_config_title">Konfiguracja ocen</string>
<string name="grades_ects_points_format">Punkty ECTS: %#.2f</string>
<string name="grades_editor_add_grade">Dodaj ocenę</string>
<string name="grades_editor_add_grade_title">Dodawanie oceny</string>
<string name="grades_editor_add_grade_weight">Podaj wagę oceny</string>
@ -474,9 +475,11 @@
<string name="grades_semester_average_point_format">semestr %d: %spkt</string>
<string name="grades_semester_format">Semestr %d</string>
<string name="grades_semester_header_format">Semestr %d</string>
<string name="grades_stats_all_grades">wszystkie oceny</string>
<string name="grades_stats_custom_config_notice">Aktualne ustawienia ocen mogą wpływać na średnią. Jeśli uważasz, że się ona nie zgadza, kliknij Konfiguruj.</string>
<string name="grades_stats_custom_value_notice">Została ustawiona własna wartość plusa/minusa. Jeśli uważasz, że się ona nie zgadza, kliknij Konfiguruj.</string>
<string name="grades_stats_disclaimer">*średnie ocen są poglądowe i mogą się różnić, w zależności od ustawień szkoły</string>
<string name="grades_stats_ects_points">punkty ECTS</string>
<string name="grades_stats_expected">*przewidywana średnia</string>
<string name="grades_stats_from_final">*z ocen końcowych\nPrzewidywana: %s</string>
<string name="grades_stats_from_final_no_expected">*z ocen końcowych</string>
@ -490,7 +493,9 @@
<string name="grades_stats_proposed_avg">Śr. ocen proponowanych:\n%s</string>
<string name="grades_stats_semester_1">semestr 1</string>
<string name="grades_stats_semester_2">semestr 2</string>
<string name="grades_stats_this_semester">ten semestr</string>
<string name="grades_stats_title">Statystyka ocen</string>
<string name="grades_stats_university">Średnia ocen za studia</string>
<string name="grades_stats_yearly">całoroczna</string>
<string name="grades_value_format">wartość: %s</string>
<string name="grades_weight_format">waga %s</string>