Admin: name submission archives as group-studentNo-name-attempt
This commit is contained in:
@@ -746,16 +746,36 @@ class AdminController(
|
|||||||
@GetMapping("/submissions/{id}/download")
|
@GetMapping("/submissions/{id}/download")
|
||||||
fun downloadSubmissionAsAdmin(
|
fun downloadSubmissionAsAdmin(
|
||||||
@PathVariable id: String,
|
@PathVariable id: String,
|
||||||
|
@RequestParam(required = false) group: String?,
|
||||||
|
@RequestParam(required = false) no: String?,
|
||||||
|
@RequestParam(required = false) name: String?,
|
||||||
|
@RequestParam(required = false) attempt: String?,
|
||||||
session: HttpSession,
|
session: HttpSession,
|
||||||
): ResponseEntity<ByteArray> {
|
): ResponseEntity<ByteArray> {
|
||||||
val file = api.downloadSubmissionAsAdmin(token(session), id)
|
val file = api.downloadSubmissionAsAdmin(token(session), id)
|
||||||
val encoded = java.net.URLEncoder.encode(file.filename, java.nio.charset.StandardCharsets.UTF_8)
|
val filename = submissionFilename(group, no, name, attempt) ?: file.filename
|
||||||
|
val encoded = java.net.URLEncoder.encode(filename, java.nio.charset.StandardCharsets.UTF_8)
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''$encoded")
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''$encoded")
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.body(file.bytes)
|
.body(file.bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Человекочитаемое имя архива сдачи: группа-номер студенческого-ФИО-попыткаN.zip */
|
||||||
|
private fun submissionFilename(group: String?, no: String?, name: String?, attempt: String?): String? {
|
||||||
|
val parts = listOfNotNull(
|
||||||
|
group?.takeIf { it.isNotBlank() },
|
||||||
|
no?.takeIf { it.isNotBlank() },
|
||||||
|
name?.takeIf { it.isNotBlank() },
|
||||||
|
attempt?.takeIf { it.isNotBlank() }?.let { "попытка$it" },
|
||||||
|
)
|
||||||
|
if (parts.isEmpty()) return null
|
||||||
|
val base = parts.joinToString("-")
|
||||||
|
.replace(Regex("[\\\\/:*?\"<>|\\r\\n]"), "_")
|
||||||
|
.trim()
|
||||||
|
return if (base.isBlank()) null else "$base.zip"
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/labs/{labId}/assign")
|
@PostMapping("/labs/{labId}/assign")
|
||||||
fun assignVariant(
|
fun assignVariant(
|
||||||
@PathVariable labId: String,
|
@PathVariable labId: String,
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<span th:if="${cell.defenseStatus == 'started'}" class="cell-chip chip-warn" title="Защита: студент пишет работу">🛡⏳</span>
|
<span th:if="${cell.defenseStatus == 'started'}" class="cell-chip chip-warn" title="Защита: студент пишет работу">🛡⏳</span>
|
||||||
<span th:if="${cell.defenseStatus == 'ready'}" class="cell-chip chip-ok" title="Защита: работа сохранена, можно принимать">🛡✓</span>
|
<span th:if="${cell.defenseStatus == 'ready'}" class="cell-chip chip-ok" title="Защита: работа сохранена, можно принимать">🛡✓</span>
|
||||||
<a th:if="${cell.submissionId}" class="cell-chip chip-link"
|
<a th:if="${cell.submissionId}" class="cell-chip chip-link"
|
||||||
th:href="'/admin/submissions/' + ${cell.submissionId} + '/download'"
|
th:href="@{/admin/submissions/{id}/download(id=${cell.submissionId}, group=${group.name}, no=${row.studentNo}, name=${row.fullName}, attempt=${cell.submissionSeq})}"
|
||||||
title="Скачать последнее решение студента">↓<span class="font-mono" th:text="${cell.submissionSeq}">1</span></a>
|
title="Скачать последнее решение студента">↓<span class="font-mono" th:text="${cell.submissionSeq}">1</span></a>
|
||||||
<span th:unless="${cell.submissionId}" class="cell-chip chip-dim" title="Решений пока не сдавалось">↓—</span>
|
<span th:unless="${cell.submissionId}" class="cell-chip chip-dim" title="Решений пока не сдавалось">↓—</span>
|
||||||
<button class="cell-chip" type="button"
|
<button class="cell-chip" type="button"
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td th:attr="data-filter-value=${s.submissionId != null ? 'сдано' : 'нет'}">
|
<td th:attr="data-filter-value=${s.submissionId != null ? 'сдано' : 'нет'}">
|
||||||
<a th:if="${s.submissionId}" class="cell-link"
|
<a th:if="${s.submissionId}" class="cell-link"
|
||||||
th:href="'/admin/submissions/' + ${s.submissionId} + '/download'">
|
th:href="@{/admin/submissions/{id}/download(id=${s.submissionId}, group=${g.groupName}, no=${s.studentNo}, name=${s.fullName}, attempt=${s.submissionSeq})}">
|
||||||
попытка <span th:text="${s.submissionSeq}">1</span> ↓</a>
|
попытка <span th:text="${s.submissionSeq}">1</span> ↓</a>
|
||||||
<div th:if="${!#lists.isEmpty(s.submissionFiles)}" class="text-muted text-sm font-mono">
|
<div th:if="${!#lists.isEmpty(s.submissionFiles)}" class="text-muted text-sm font-mono">
|
||||||
<span th:text="${#strings.listJoin(s.submissionFiles, ', ')}">main.c</span>
|
<span th:text="${#strings.listJoin(s.submissionFiles, ', ')}">main.c</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user