Refactor: split loop/throws helpers; drop deprecated JSON accessors

This commit is contained in:
2026-09-27 22:02:56 +03:00
parent 511df7977e
commit 9a2a4023b5
2 changed files with 59 additions and 27 deletions

View File

@@ -124,10 +124,22 @@ class DefenseService(
studentId: UUID, studentId: UUID,
code: String, code: String,
): DefenseSession { ): DefenseSession {
val s = sessions.findByCode(code.trim()) ?: throw BadRequestException("Код не найден") val session = findSessionByCode(code)
if (s.studentId != studentId) throw ForbiddenException("Этот код выдан другому студенту") requireOwnerAndWaiting(session, studentId)
if (s.status != DefenseStatus.waiting.name) throw BadRequestException("Защита по этому коду уже начата") return session
return s }
private fun findSessionByCode(code: String): DefenseSession =
sessions.findByCode(code.trim()) ?: throw BadRequestException("Код не найден")
private fun requireOwnerAndWaiting(
session: DefenseSession,
studentId: UUID,
) {
if (session.studentId != studentId) throw ForbiddenException("Этот код выдан другому студенту")
if (session.status != DefenseStatus.waiting.name) {
throw BadRequestException("Защита по этому коду уже начата")
}
} }
@Transactional @Transactional

View File

@@ -20,6 +20,7 @@ import ru.ysu.lms.web.api.CreateStudentRequestDto
import ru.ysu.lms.web.api.DistributeRequestPayload import ru.ysu.lms.web.api.DistributeRequestPayload
import ru.ysu.lms.web.api.GradeSetPayload import ru.ysu.lms.web.api.GradeSetPayload
import ru.ysu.lms.web.api.LabCreatePayload import ru.ysu.lms.web.api.LabCreatePayload
import ru.ysu.lms.web.api.LabSummaryDto
import ru.ysu.lms.web.api.LabUpdatePayload import ru.ysu.lms.web.api.LabUpdatePayload
import ru.ysu.lms.web.api.LevelAssignPayload import ru.ysu.lms.web.api.LevelAssignPayload
import ru.ysu.lms.web.api.LevelPayload import ru.ysu.lms.web.api.LevelPayload
@@ -479,29 +480,13 @@ class AdminController(
var count = 0 var count = 0
var skipped = 0 var skipped = 0
for ((idx, rawCols) in rows.withIndex()) { for ((idx, rawCols) in rows.withIndex()) {
val cells = rawCols.map { it.trim() } val row = resolveDefenseRow(rawCols, idx, labId, studentByNo, labByNumber)
if (cells.all { it.isBlank() }) continue if (row == null) {
if (idx == 0 && looksLikeHeader(cells)) continue
var sid: String? = null
var lid: String? = labId?.takeIf { it.isNotBlank() }
var md: String? = null
when {
cells.size >= 3 -> {
sid = studentByNo[cells[0]]
lid = labByNumber[cells[1]]?.id ?: lid
md = cells.drop(2).joinToString("; ")
}
cells.size == 2 -> {
sid = studentByNo[cells[0]]
md = cells[1]
}
}
if (sid == null || lid.isNullOrBlank() || md.isNullOrBlank()) {
skipped++ skipped++
continue } else {
api.assignDefense(t, row.studentId, row.labId, row.assignmentMd.replace("\\n", "\n"))
count++
} }
api.assignDefense(t, sid, lid, md.replace("\\n", "\n"))
count++
} }
val tail = if (skipped > 0) ", пропущено: $skipped" else "" val tail = if (skipped > 0) ", пропущено: $skipped" else ""
redirect.addFlashAttribute("notice", "Импортировано заданий: $count$tail") redirect.addFlashAttribute("notice", "Импортировано заданий: $count$tail")
@@ -511,6 +496,41 @@ class AdminController(
redirectToGroup(groupId, labId) redirectToGroup(groupId, labId)
} }
private data class DefenseRow(
val studentId: String,
val labId: String,
val assignmentMd: String,
)
/** Разбирает строку CSV с заданием на защиту; null — строку нужно пропустить. */
private fun resolveDefenseRow(
rawCols: List<String>,
index: Int,
defaultLabId: String?,
studentByNo: Map<String, String>,
labByNumber: Map<String, LabSummaryDto>,
): DefenseRow? {
val cells = rawCols.map { it.trim() }
if (cells.all { it.isBlank() }) return null
if (index == 0 && looksLikeHeader(cells)) return null
var sid: String? = null
var lid: String? = defaultLabId?.takeIf { it.isNotBlank() }
var md: String? = null
when {
cells.size >= 3 -> {
sid = studentByNo[cells[0]]
lid = labByNumber[cells[1]]?.id ?: lid
md = cells.drop(2).joinToString("; ")
}
cells.size == 2 -> {
sid = studentByNo[cells[0]]
md = cells[1]
}
}
if (sid == null || lid.isNullOrBlank() || md.isNullOrBlank()) return null
return DefenseRow(sid, lid, md)
}
private fun looksLikeHeader(cells: List<String>): Boolean { private fun looksLikeHeader(cells: List<String>): Boolean {
val joined = cells.joinToString(" ").lowercase() val joined = cells.joinToString(" ").lowercase()
return joined.contains("студент") || return joined.contains("студент") ||
@@ -1250,11 +1270,11 @@ class AdminController(
val node = mapper.readTree(json) val node = mapper.readTree(json)
buildList { buildList {
node.forEach { el -> node.forEach { el ->
val text = el.path("text").asText("").trim() val text = el.path("text").asString("").trim()
if (text.isNotEmpty()) { if (text.isNotEmpty()) {
add( add(
ru.ysu.lms.web.api ru.ysu.lms.web.api
.CommentPayload(text, el.path("tone").asText("neutral")), .CommentPayload(text, el.path("tone").asString("neutral")),
) )
} }
} }