Solutions zip: use real file/submission timestamps instead of epoch (fixes bogus years)

This commit is contained in:
2026-09-29 18:31:19 +03:00
parent fca585921d
commit b97a0096ff
2 changed files with 21 additions and 10 deletions

View File

@@ -43,16 +43,18 @@ class SubmissionStorage(
return StoredSubmission(dir, names) return StoredSubmission(dir, names)
} }
/** Собирает zip из сохранённых файлов сдачи. */ /** Собирает zip из сохранённых файлов сдачи, сохраняя реальное время изменения файлов. */
fun zipOf(storedDir: Path): ByteArray { fun zipOf(storedDir: Path): ByteArray {
val out = ByteArrayOutputStream() val out = ByteArrayOutputStream()
ZipOutputStream(out).use { zos -> ZipOutputStream(out).use { zos ->
Files.list(storedDir).forEach { file -> Files.list(storedDir).use { stream ->
ZipEntry(file.fileName.toString()).also { entry -> stream.forEach { file ->
entry.time = 0 ZipEntry(file.fileName.toString()).also { entry ->
zos.putNextEntry(entry) entry.time = Files.getLastModifiedTime(file).toMillis()
Files.copy(file, zos) zos.putNextEntry(entry)
zos.closeEntry() Files.copy(file, zos)
zos.closeEntry()
}
} }
} }
} }

View File

@@ -23,6 +23,7 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.net.URLEncoder import java.net.URLEncoder
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.time.Instant
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
@@ -164,14 +165,14 @@ abstract class AdminSupport(
): ByteArray { ): ByteArray {
val out = ByteArrayOutputStream() val out = ByteArrayOutputStream()
ZipOutputStream(out).use { zos -> ZipOutputStream(out).use { zos ->
zos.putNextEntry(ZipEntry("ОПИСАНИЕ.txt").apply { time = 0 }) zos.putNextEntry(ZipEntry("ОПИСАНИЕ.txt"))
zos.write("Архив сдач группы $groupName (последние попытки)\n".toByteArray(Charsets.UTF_8)) zos.write("Архив сдач группы $groupName (последние попытки)\n".toByteArray(Charsets.UTF_8))
zos.closeEntry() zos.closeEntry()
for (lab in labs) { for (lab in labs) {
for (sub in api.labSubmissions(token, lab.id)) { for (sub in api.labSubmissions(token, lab.id)) {
val student = studentById[sub.studentId] ?: continue val student = studentById[sub.studentId] ?: continue
val bytes = api.downloadSubmissionAsAdmin(token, sub.id).bytes val bytes = api.downloadSubmissionAsAdmin(token, sub.id).bytes
appendSubmissionArchive(zos, lab, student, bytes) appendSubmissionArchive(zos, lab, student, bytes, sub.createdAt)
} }
} }
} }
@@ -183,13 +184,21 @@ abstract class AdminSupport(
lab: LabSummaryDto, lab: LabSummaryDto,
student: StudentRowDto, student: StudentRowDto,
submissionZip: ByteArray, submissionZip: ByteArray,
submissionCreatedAt: String? = null,
) { ) {
val fallbackMillis =
submissionCreatedAt
?.let { runCatching { Instant.parse(it).toEpochMilli() }.getOrNull() }
?: System.currentTimeMillis()
ZipInputStream(ByteArrayInputStream(submissionZip)).use { zin -> ZipInputStream(ByteArrayInputStream(submissionZip)).use { zin ->
var entry = zin.nextEntry var entry = zin.nextEntry
while (entry != null) { while (entry != null) {
if (!entry.isDirectory) { if (!entry.isDirectory) {
val safeName = student.fullName.replace(Regex("[\\\\/:*?\"<>|]"), "_") val safeName = student.fullName.replace(Regex("[\\\\/:*?\"<>|]"), "_")
zos.putNextEntry(ZipEntry("ЛР${lab.number}/${student.studentNo}_$safeName/${entry.name}").apply { time = 0 }) val entryMillis = entry.time.takeIf { it > 0 } ?: fallbackMillis
zos.putNextEntry(
ZipEntry("ЛР${lab.number}/${student.studentNo}_$safeName/${entry.name}").apply { time = entryMillis },
)
zin.copyTo(zos) zin.copyTo(zos)
zos.closeEntry() zos.closeEntry()
} }