fix: keep group page working when attestations fail

- tolerate legacy textual semester values from older progress-service responses
- treat attestations as optional data on the group page and log loading failures instead of returning 500
This commit is contained in:
2026-10-26 17:37:57 +03:00
parent dc13ba3186
commit 9e16566160
5 changed files with 26 additions and 4 deletions

View File

@@ -43,6 +43,13 @@ object AttestationPresentation {
fun kindLabel(kind: String): String = KIND_LABELS[kind] ?: kind
/** Понимает и «2», и старые значения вроде «2 семестр»; вне 1–8 — 1. */
fun semesterNumber(raw: String): Int {
val digits = raw.filter { it.isDigit() }
val value = digits.toIntOrNull() ?: return 1
return if (value in 1..8) value else 1
}
fun valueLabel(
kind: String,
value: String,

View File

@@ -1,6 +1,7 @@
package ru.ysu.lms.web.admin
import jakarta.servlet.http.HttpSession
import org.slf4j.LoggerFactory
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
@@ -59,6 +60,8 @@ class AdminGroupController(
markdown: MarkdownRenderer,
mapper: ObjectMapper,
) : AdminSupport(api, markdown, mapper) {
private val log = LoggerFactory.getLogger(AdminGroupController::class.java)
@GetMapping
fun dashboard(
session: HttpSession,
@@ -143,7 +146,7 @@ class AdminGroupController(
val allLabs = api.listLabs(t)
val group = api.groupDetail(t, id)
val labs = allLabs.filter { it.status == "published" }
val attestations = GroupAttestationViews.columns(api.listAttestations(t, id))
val attestations = loadAttestations(t, id)
val grades = api.listGrades(t)
val gradeBy = grades.associate { "${it.studentId}|${it.labId}" to it.score }
val generalCommentBy = grades.associate { "${it.studentId}|${it.labId}" to it.generalComment }
@@ -260,6 +263,17 @@ class AdminGroupController(
)
}
/** Аттестации не должны ронять всю ведомость, если сервис ещё не обновлён. */
private fun loadAttestations(
token: String,
groupId: String,
): List<GroupAttestation> =
GroupAttestationViews.columns(
runCatching { api.listAttestations(token, groupId) }
.onFailure { log.warn("Не удалось загрузить аттестации группы {}", groupId, it) }
.getOrDefault(emptyList()),
)
private fun averageScore(cells: List<GroupLabCell>): String? {
val scores = cells.mapNotNull { it.score?.replace(',', '.')?.toBigDecimalOrNull() }
if (scores.isEmpty()) return null

View File

@@ -18,7 +18,7 @@ object GroupAttestationViews {
id = column.id,
kind = column.kind,
kindLabel = AttestationPresentation.kindLabel(column.kind),
semester = column.semester,
semester = AttestationPresentation.semesterNumber(column.semester),
published = column.published,
options =
VALUES_BY_KIND[column.kind].orEmpty().map { value ->

View File

@@ -275,7 +275,8 @@ data class AttestationDto(
val id: String = "",
val groupId: String = "",
val kind: String = "",
val semester: Int = 1,
/** Строка — чтобы старые ответы с текстовым семестром не ломали разбор. */
val semester: String = "1",
val published: Boolean = false,
val results: List<AttestationResultDto> = emptyList(),
)

View File

@@ -94,7 +94,7 @@ class StudentController(
val result = column.results.firstOrNull()
StudentAttestationView(
kindLabel = AttestationPresentation.kindLabel(column.kind),
semester = column.semester,
semester = AttestationPresentation.semesterNumber(column.semester),
valueLabel = result?.let { AttestationPresentation.valueLabel(column.kind, it.value) } ?: "—",
positive = result?.let { AttestationPresentation.isPositive(column.kind, it.value) },
)