Skip to content

Commit 574ff77

Browse files
committed
Implement pruning of older submissions to save space
1 parent 773c0c8 commit 574ff77

File tree

4 files changed

+49
-2
lines changed

4 files changed

+49
-2
lines changed

src/main/kotlin/ch/uzh/ifi/access/config/SecurityConfig.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ class SecurityConfig(private val env: Environment) {
8383
).permitAll()
8484
.requestMatchers(
8585
"/courses/{course}/participants/**",
86-
"/courses/{course}/summary"
86+
"/courses/{course}/summary",
87+
"/pruneSubmissions"
8788
).access { _, context ->
8889
AuthorityAuthorizationDecision(isAuthorizedAPIKey(context), parseAuthorities(listOf("supervisor")))
8990
}

src/main/kotlin/ch/uzh/ifi/access/controller/CourseController.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ class CourseRootController(
2929
return courseService.editCourse(courseDTO).slug
3030
}
3131

32+
@GetMapping("/pruneSubmissions")
33+
fun pruneSubmissions(): String? {
34+
courseService.globalPruneSubmissions()
35+
return "done"
36+
}
37+
3238
@PostMapping("/contact")
3339
fun sendMessage(@RequestBody contactDTO: ContactDTO?) {
3440
courseService.sendMessage(contactDTO!!)

src/main/kotlin/ch/uzh/ifi/access/model/Evaluation.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ class Evaluation {
4343
fun addSubmission(newSubmission: Submission): Submission {
4444
submissions.add(newSubmission)
4545
newSubmission.evaluation = this
46-
newSubmission.ordinalNum = countSubmissionsByType(newSubmission.command!!)
46+
val latestOfType = submissions.filter { it.command == newSubmission.command }.maxByOrNull { it.ordinalNum ?: 0 }
47+
newSubmission.ordinalNum = (latestOfType?.ordinalNum ?: 0) + 1
4748
return newSubmission
4849
}
4950

src/main/kotlin/ch/uzh/ifi/access/service/CourseService.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,44 @@ class CourseService(
338338
submissionRepository.saveAndFlush(submission)
339339
}
340340

341+
// only necessary once to prune existing data in already-deployed ACCESS
342+
fun globalPruneSubmissions() {
343+
evaluationRepository.findAll().forEach { if (it != null) {pruneSubmissions(it) } }
344+
}
345+
346+
fun pruneSubmissions(evaluation: Evaluation) {
347+
// keep all GRADE submissions, but prune RUN and TEST submissions
348+
listOf(Command.RUN, Command.TEST, Command.GRADE).map { command ->
349+
Pair(command, evaluation.submissions.filter { it.command == command }.sortedByDescending { it.ordinalNum })
350+
}.filter { it.second.isNotEmpty() }.forEach { (command, submissions) ->
351+
val latest = submissions.first().ordinalNum!!
352+
// always keep the latest 5
353+
val keepOrdinalNums = submissions.take(5).map { it.ordinalNum!! }.toMutableSet()
354+
// always keep the (latest) best GRADE submission
355+
if (command == Command.GRADE) {
356+
val best = submissions.maxWith(compareBy<Submission> { it.points }.thenBy { it.ordinalNum })
357+
keepOrdinalNums.add(best.ordinalNum!!)
358+
}
359+
// keep increasingly fewer submissions the older they are (none older than 100 submissions ago)
360+
var low = 5
361+
listOf(10, 15, 25, 50, 75, 100).forEach { high ->
362+
// find oldest in current low < distance_to_latest <= high bracket
363+
val highestOrdinalNum = submissions.lastOrNull {
364+
val distance = latest - it.ordinalNum!!
365+
distance in low..<high
366+
}?.ordinalNum
367+
if (highestOrdinalNum != null) {
368+
keepOrdinalNums.add(highestOrdinalNum)
369+
}
370+
low = high
371+
}
372+
val prune = submissions.filter { !keepOrdinalNums.contains(it.ordinalNum!!) }
373+
evaluation.submissions.removeAll(prune)
374+
submissionRepository.deleteAll(prune)
375+
logger.debug { " -->> Pruning ${evaluation.userId}/${evaluation.task?.assignment?.slug}/${evaluation.task?.slug} [${command}]: ${prune.map{it.ordinalNum}}" }
376+
}
377+
}
378+
341379
@Caching(evict = [
342380
CacheEvict(value = ["getStudent"], key = "#courseSlug + '-' + #submissionDTO.userId"),
343381
CacheEvict(value = ["getStudentWithPoints"], key = "#courseSlug + '-' + #submissionDTO.userId"),
@@ -366,6 +404,7 @@ class CourseService(
366404
)
367405
}
368406
val submission = submissionRepository.saveAndFlush(newSubmission)
407+
pruneSubmissions(evaluation)
369408
submissionDTO.files.stream().filter { fileDTO -> fileDTO.content != null }
370409
.forEach { fileDTO: SubmissionFileDTO -> createSubmissionFile(submission, fileDTO) }
371410
submission.valid = !submission.isGraded

0 commit comments

Comments
 (0)