Skip to content

Commit 8b07a1c

Browse files
authored
Examples submission count
* Rebased branch on dev * Completed #179 - Added example submissions count endpoint - Added getSubmissionsCount with native sql query - Added new DTO - Updated cache evicts - Added caching for example submissions count * Added submissions count cache evict to example reset * Added grace period to submissions count query
1 parent 167c34d commit 8b07a1c

File tree

6 files changed

+63
-10
lines changed

6 files changed

+63
-10
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class CacheConfig {
3232
"PointsService.calculateAssignmentMaxPoints",
3333
"CourseService.getStudents",
3434
"CourseService.calculateCoursePoints",
35+
"ExampleService.computeSubmissionsCount",
3536
"CourseService.getCoursesOverview",
3637
"ExampleService.studentHasVisibleExamples",
3738
"ExampleService.supervisorHasVisibleExamples",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ class CourseController(
227227
}
228228

229229
@PostMapping("/{course}/participants")
230-
@CacheEvict(value = ["CourseService.getStudents"], key = "#course")
230+
@CacheEvict(value = ["CourseService.getStudents", "ExampleService.computeSubmissionsCount"], key = "#course")
231231
fun registerParticipants(@PathVariable course: String, @RequestBody registrationIDs: List<String>) {
232232
return updateRoles(course, registrationIDs, Role.STUDENT)
233233
}

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi
1212
import kotlinx.coroutines.GlobalScope
1313
import kotlinx.coroutines.launch
1414
import org.springframework.cache.annotation.CacheEvict
15+
import org.springframework.cache.annotation.Caching
1516
import org.springframework.http.HttpStatus
1617
import org.springframework.scheduling.annotation.EnableAsync
1718
import org.springframework.security.access.prepost.PreAuthorize
@@ -45,6 +46,15 @@ class ExampleController(
4546
return exampleService.getExamples(course)
4647
}
4748

49+
@GetMapping("/submissions-count")
50+
@PreAuthorize("hasRole(#course+'-assistant')")
51+
fun getSubmissionsCount(
52+
@PathVariable course: String,
53+
authentication: Authentication
54+
): ExampleSubmissionsCountDTO {
55+
return exampleService.computeSubmissionsCount(course)
56+
}
57+
4858
@GetMapping("/interactive")
4959
@PreAuthorize("hasRole(#course)")
5060
fun getInteractiveExampleSlug(
@@ -101,6 +111,7 @@ class ExampleController(
101111

102112
@PostMapping("/{example}/submit")
103113
@PreAuthorize("hasRole(#course) and (#submission.restricted or hasRole(#course + '-assistant'))")
114+
@CacheEvict(value = ["ExampleService.computeSubmissionsCount"], key = "#course")
104115
fun evaluateExampleSubmission(
105116
@PathVariable course: String,
106117
@PathVariable example: String,
@@ -250,7 +261,12 @@ class ExampleController(
250261

251262
@DeleteMapping("/{example}/reset")
252263
@PreAuthorize("hasRole(#course+'-supervisor')")
253-
@CacheEvict(value = ["PointsService.calculateTaskPoints"], allEntries = true)
264+
@Caching(
265+
evict = [
266+
CacheEvict(value = ["PointsService.calculateTaskPoints"], allEntries = true),
267+
CacheEvict(value = ["ExampleService.computeSubmissionsCount"], key = "#course")
268+
]
269+
)
254270
fun resetExample(
255271
@PathVariable course: String,
256272
@PathVariable example: String
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package ch.uzh.ifi.access.model.dto
2+
3+
class ExampleSubmissionsCountDTO(
4+
var submissionsCount: MutableMap<String, Int> = HashMap()
5+
)

src/main/kotlin/ch/uzh/ifi/access/repository/ExampleRepository.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ch.uzh.ifi.access.model.Task
44
import ch.uzh.ifi.access.projections.TaskOverview
55
import ch.uzh.ifi.access.projections.TaskWorkspace
66
import org.springframework.data.jpa.repository.JpaRepository
7+
import org.springframework.data.jpa.repository.Query
78

89
interface ExampleRepository : JpaRepository<Task?, Long?> {
910

@@ -20,4 +21,19 @@ interface ExampleRepository : JpaRepository<Task?, Long?> {
2021
courseSlug: String?,
2122
exampleSlug: String?
2223
): Task?
24+
25+
@Query(
26+
"""
27+
SELECT submission.user_id, COUNT(*) AS entry_count FROM task
28+
JOIN course ON course.id = task.course_id AND course.slug = :courseSlug
29+
JOIN evaluation ON evaluation.task_id = task.id
30+
JOIN submission ON submission.evaluation_id = evaluation.id AND submission.created_at >= task.start_date AND submission.created_at <= task.end_date + (:gracePeriod * INTERVAL '1 second')
31+
GROUP BY submission.user_id
32+
""",
33+
nativeQuery = true
34+
)
35+
fun getSubmissionsCount(
36+
courseSlug: String,
37+
gracePeriod: Long,
38+
): List<Map<String, Any>>
2339
}

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class ExampleService(
4747
}
4848

4949
@Cacheable(value = ["ExampleService.getInteractiveExampleSlug"], key = "#courseSlug")
50-
fun getInteractiveExampleSlug(courseSlug: String):InteractiveExampleDTO{
50+
fun getInteractiveExampleSlug(courseSlug: String): InteractiveExampleDTO {
5151
val examples = getExamples(courseSlug)
5252
val now = LocalDateTime.now()
5353
val interactiveExampleSlug = examples
@@ -57,18 +57,18 @@ class ExampleService(
5757
return InteractiveExampleDTO(interactiveExampleSlug)
5858
}
5959

60-
@Cacheable(value = ["ExampleService.studentHasVisibleExamples"], key="#courseSlug")
61-
fun studentHasVisibleExamples(courseSlug: String):Boolean{
62-
return getExamples(courseSlug).any{it.start != null}
60+
@Cacheable(value = ["ExampleService.studentHasVisibleExamples"], key = "#courseSlug")
61+
fun studentHasVisibleExamples(courseSlug: String): Boolean {
62+
return getExamples(courseSlug).any { it.start != null }
6363
}
6464

65-
@Cacheable(value = ["ExampleService.supervisorHasVisibleExamples"], key="#courseSlug")
66-
fun supervisorHasVisibleExamples(courseSlug: String):Boolean{
65+
@Cacheable(value = ["ExampleService.supervisorHasVisibleExamples"], key = "#courseSlug")
66+
fun supervisorHasVisibleExamples(courseSlug: String): Boolean {
6767
return getExamples(courseSlug).isNotEmpty()
6868
}
6969

70-
fun hasVisibleExamples(courseSlug: String):Boolean {
71-
if(roleService.isSupervisor(courseSlug)){
70+
fun hasVisibleExamples(courseSlug: String): Boolean {
71+
if (roleService.isSupervisor(courseSlug)) {
7272
return proxy.supervisorHasVisibleExamples(courseSlug)
7373
}
7474

@@ -223,6 +223,21 @@ class ExampleService(
223223
return newSubmission
224224
}
225225

226+
@Cacheable(value = ["ExampleService.computeSubmissionsCount"], key = "#courseSlug")
227+
fun computeSubmissionsCount(courseSlug: String): ExampleSubmissionsCountDTO {
228+
val res = exampleRepository.getSubmissionsCount(courseSlug, gracePeriod)
229+
230+
val submissionsCount: Map<String, Int> = res.associate { row ->
231+
val userId = row["user_id"] as String
232+
val count = (row["entry_count"] as Number).toInt()
233+
userId to count
234+
}
235+
236+
return ExampleSubmissionsCountDTO(
237+
submissionsCount = submissionsCount.toMutableMap()
238+
)
239+
}
240+
226241
fun computeExampleInformation(courseSlug: String, exampleSlug: String): ExampleInformationDTO {
227242
val participantsOnline = visitQueueService.getRecentlyActiveCount(courseSlug)
228243
val totalParticipants = courseService.getCourseBySlug(courseSlug).participantCount

0 commit comments

Comments
 (0)