diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 43bbb0b3..8d9ab44b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -20,6 +20,7 @@ import dev.dres.api.rest.handler.log.ResultLogHandler import dev.dres.api.rest.handler.preview.* import dev.dres.api.rest.handler.scores.ListEvaluationScoreHandler import dev.dres.api.rest.handler.submission.SubmissionHandler +import dev.dres.api.rest.handler.submission.SubmissionStatusHandler import dev.dres.api.rest.handler.system.CurrentTimeHandler import dev.dres.api.rest.handler.system.InfoHandler import dev.dres.api.rest.handler.system.LoginHandler @@ -148,7 +149,11 @@ object RestApi { GetTeamLogoHandler(), // Submission - SubmissionHandler(store), + SubmissionHandler(), + + // Submission Status + SubmissionStatusHandler(store), + SubmissionStatusHandler(store), // Log QueryLogHandler(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionAllStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionAllStatusHandler.kt new file mode 100644 index 00000000..1ed91e5e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionAllStatusHandler.kt @@ -0,0 +1,72 @@ +package dev.dres.api.rest.handler.submission + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.RestApi +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmissionList +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +class SubmissionAllStatusHandler(private val store: TransientEntityStore) : GetRestHandler, + AccessManagedRestHandler { + + override val permittedRoles = setOf(ApiRole.PARTICIPANT) + + override val apiVersion = RestApi.LATEST_API_VERSION + + override val route = "submission/{evaluationId}/all" + + + @OpenApi( + summary = "Endpoint provide the information about all submissions of a team.", + path = "/api/v2/submission/{evaluationId}/{submissionId}", + methods = [HttpMethod.GET], + operationId = OpenApiOperation.AUTO_GENERATE, + pathParams = [ + OpenApiParam( + "evaluationId", + String::class, + "The ID of the evaluation the submission belongs to.", + required = true + ), + ], + queryParams = [ + OpenApiParam("session", String::class, "Session Token") + ], + responses = [ + OpenApiResponse( + "200", + [OpenApiContent(ApiSubmissionList::class)], + description = "The submissions for this evaluation." + ), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), + ], + tags = ["Submission"] + ) + override fun doGet(ctx: Context): ApiSubmissionList { + + val rac = ctx.runActionContext() + + val runManager = AccessManager.getRunManagerForUser(rac.userId).find { it.id == rac.evaluationId } + ?: throw ErrorStatusException(404, "Evaluation with ID '${rac.evaluationId}' could not be found.", ctx) + + val teamId = runManager.template.teams.singleOrNull { it.users.any { u -> u.id == rac.userId } }?.id + + if (teamId == null) { + throw ErrorStatusException(404, "No valid team found in evaluation with ID '${rac.evaluationId}'.", ctx) + } + + val submissions = this.store.transactional(true) { + runManager.allSubmissions().asSequence().filter { it.teamId == teamId }.map { it.toApi() }.toList() + } + + return ApiSubmissionList(submissions) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 339e269d..d64cb7e4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -6,15 +6,10 @@ import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiSubmission -import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.AuditLogSource import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -25,14 +20,9 @@ import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.first -import kotlinx.dnq.query.firstOrNull -import kotlinx.dnq.transactional import org.slf4j.LoggerFactory -class SubmissionHandler(private val store: TransientEntityStore) : PostRestHandler, +class SubmissionHandler : PostRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.PARTICIPANT) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionStatusHandler.kt new file mode 100644 index 00000000..950de57f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionStatusHandler.kt @@ -0,0 +1,83 @@ +package dev.dres.api.rest.handler.submission + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.RestApi +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +class SubmissionStatusHandler(private val store: TransientEntityStore) : GetRestHandler, + AccessManagedRestHandler { + + override val permittedRoles = setOf(ApiRole.PARTICIPANT) + + override val apiVersion = RestApi.LATEST_API_VERSION + + override val route = "submission/{evaluationId}/{submissionId}" + + + @OpenApi( + summary = "Endpoint provide the information about a given submission.", + path = "/api/v2/submission/{evaluationId}/{submissionId}", + methods = [HttpMethod.GET], + operationId = OpenApiOperation.AUTO_GENERATE, + pathParams = [ + OpenApiParam( + "evaluationId", + String::class, + "The ID of the evaluation the submission belongs to.", + required = true + ), + OpenApiParam( + "submissionId", + String::class, + "The ID of the submission.", + required = true + ), + ], + queryParams = [ + OpenApiParam("session", String::class, "Session Token") + ], + responses = [ + OpenApiResponse( + "200", + [OpenApiContent(ApiSubmission::class)], + description = "The submission." + ), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), + ], + tags = ["Submission"] + ) + override fun doGet(ctx: Context): ApiSubmission { + + val rac = ctx.runActionContext() + + val runManager = AccessManager.getRunManagerForUser(rac.userId).find { it.id == rac.evaluationId } + ?: throw ErrorStatusException(404, "Evaluation with ID '${rac.evaluationId}' could not be found.", ctx) + + val submissionId = ctx.pathParam("submissionId") + + val submission = this.store.transactional(true) { + runManager.allSubmissions().find { it.id == submissionId }?.toApi() + } + + if (submission == null) { + throw ErrorStatusException(404, "Submission with ID '${submissionId}' not found.", ctx) + } + + val teamId = runManager.template.teams.singleOrNull { it.users.any { u -> u.id == rac.userId } }?.id + + if (submission.teamId != teamId) { + throw ErrorStatusException(404, "No valid submission found", ctx) + } + + return submission + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmissionList.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmissionList.kt new file mode 100644 index 00000000..c51f5fe0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmissionList.kt @@ -0,0 +1,6 @@ +package dev.dres.api.rest.types.evaluation.submission + +import kotlinx.serialization.Serializable + +@Serializable +data class ApiSubmissionList(val submissions: List) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 9534475d..97d8346b 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -461,10 +461,9 @@ class InteractiveAsynchronousRunManager( /** * List of all [DbSubmission]s for this [InteractiveAsynchronousRunManager], irrespective of the [DbTask] it belongs to. * - * @param context The [RunActionContext] used for the invocation. * @return List of [DbSubmission]s. */ - override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { + override fun allSubmissions(): List = this.stateLock.read { this.evaluation.taskRuns.flatMap { it.getDbSubmissions() } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 44bd9ff5..1d76be7d 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -154,14 +154,6 @@ interface InteractiveRunManager : RunManager { */ fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: ApiVerdictStatus): Boolean - /** - * List of all [DbSubmission]s for this [InteractiveRunManager], irrespective of the [DbTask] it belongs to. - * - * @param context The [RunActionContext] used for the invocation. - * @return List of [DbSubmission]s - */ - fun allSubmissions(context: RunActionContext): List - /** * List of [DbSubmission]s for the current [DbTask]. * diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 67f11f1e..e7be65e9 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -316,7 +316,7 @@ class InteractiveSynchronousRunManager( * @param context The [RunActionContext] used for the invocation. * @return List of [DbSubmission]s. */ - override fun allSubmissions(context: RunActionContext): List = + override fun allSubmissions(): List = this.evaluation.taskRuns.flatMap { it.getDbSubmissions() } diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 520abac9..b7e35bad 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator import jetbrains.exodus.database.TransientEntityStore @@ -206,6 +207,10 @@ class NonInteractiveRunManager( } */ } + override fun allSubmissions(): List { + TODO("Not yet implemented") + } + override fun reScore(taskId: TaskId) { TODO("Not yet implemented") } diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 1e24ef66..44f9438d 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -121,6 +121,13 @@ interface RunManager : Runnable { */ fun postSubmission(context: RunActionContext, submission: ApiClientSubmission) : ApiSubmission + /** + * List of all [DbSubmission]s for this [InteractiveRunManager], irrespective of the [DbTask] it belongs to. + * + * @return List of [DbSubmission]s + */ + fun allSubmissions(): List + /** * Returns a list of viewer [ViewerInfo]s for this [RunManager] alongside with their respective state. *