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:
@@ -43,6 +43,13 @@ object AttestationPresentation {
|
|||||||
|
|
||||||
fun kindLabel(kind: String): String = KIND_LABELS[kind] ?: kind
|
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(
|
fun valueLabel(
|
||||||
kind: String,
|
kind: String,
|
||||||
value: String,
|
value: String,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ru.ysu.lms.web.admin
|
package ru.ysu.lms.web.admin
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpSession
|
import jakarta.servlet.http.HttpSession
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.http.HttpHeaders
|
import org.springframework.http.HttpHeaders
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
@@ -59,6 +60,8 @@ class AdminGroupController(
|
|||||||
markdown: MarkdownRenderer,
|
markdown: MarkdownRenderer,
|
||||||
mapper: ObjectMapper,
|
mapper: ObjectMapper,
|
||||||
) : AdminSupport(api, markdown, mapper) {
|
) : AdminSupport(api, markdown, mapper) {
|
||||||
|
private val log = LoggerFactory.getLogger(AdminGroupController::class.java)
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
fun dashboard(
|
fun dashboard(
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
@@ -143,7 +146,7 @@ class AdminGroupController(
|
|||||||
val allLabs = api.listLabs(t)
|
val allLabs = api.listLabs(t)
|
||||||
val group = api.groupDetail(t, id)
|
val group = api.groupDetail(t, id)
|
||||||
val labs = allLabs.filter { it.status == "published" }
|
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 grades = api.listGrades(t)
|
||||||
val gradeBy = grades.associate { "${it.studentId}|${it.labId}" to it.score }
|
val gradeBy = grades.associate { "${it.studentId}|${it.labId}" to it.score }
|
||||||
val generalCommentBy = grades.associate { "${it.studentId}|${it.labId}" to it.generalComment }
|
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? {
|
private fun averageScore(cells: List<GroupLabCell>): String? {
|
||||||
val scores = cells.mapNotNull { it.score?.replace(',', '.')?.toBigDecimalOrNull() }
|
val scores = cells.mapNotNull { it.score?.replace(',', '.')?.toBigDecimalOrNull() }
|
||||||
if (scores.isEmpty()) return null
|
if (scores.isEmpty()) return null
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ object GroupAttestationViews {
|
|||||||
id = column.id,
|
id = column.id,
|
||||||
kind = column.kind,
|
kind = column.kind,
|
||||||
kindLabel = AttestationPresentation.kindLabel(column.kind),
|
kindLabel = AttestationPresentation.kindLabel(column.kind),
|
||||||
semester = column.semester,
|
semester = AttestationPresentation.semesterNumber(column.semester),
|
||||||
published = column.published,
|
published = column.published,
|
||||||
options =
|
options =
|
||||||
VALUES_BY_KIND[column.kind].orEmpty().map { value ->
|
VALUES_BY_KIND[column.kind].orEmpty().map { value ->
|
||||||
|
|||||||
@@ -275,7 +275,8 @@ data class AttestationDto(
|
|||||||
val id: String = "",
|
val id: String = "",
|
||||||
val groupId: String = "",
|
val groupId: String = "",
|
||||||
val kind: String = "",
|
val kind: String = "",
|
||||||
val semester: Int = 1,
|
/** Строка — чтобы старые ответы с текстовым семестром не ломали разбор. */
|
||||||
|
val semester: String = "1",
|
||||||
val published: Boolean = false,
|
val published: Boolean = false,
|
||||||
val results: List<AttestationResultDto> = emptyList(),
|
val results: List<AttestationResultDto> = emptyList(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class StudentController(
|
|||||||
val result = column.results.firstOrNull()
|
val result = column.results.firstOrNull()
|
||||||
StudentAttestationView(
|
StudentAttestationView(
|
||||||
kindLabel = AttestationPresentation.kindLabel(column.kind),
|
kindLabel = AttestationPresentation.kindLabel(column.kind),
|
||||||
semester = column.semester,
|
semester = AttestationPresentation.semesterNumber(column.semester),
|
||||||
valueLabel = result?.let { AttestationPresentation.valueLabel(column.kind, it.value) } ?: "—",
|
valueLabel = result?.let { AttestationPresentation.valueLabel(column.kind, it.value) } ?: "—",
|
||||||
positive = result?.let { AttestationPresentation.isPositive(column.kind, it.value) },
|
positive = result?.let { AttestationPresentation.isPositive(column.kind, it.value) },
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user