From 31e332eaafe5baf0175e7a1668c820f8a454fe8c Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 21 Dec 2020 16:39:53 +0100 Subject: [PATCH 01/95] LoginHandler now returns UserDetails --- .../kotlin/dev/dres/api/rest/handler/LoginHandler.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt index c467f716e..d4e01a6dc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt @@ -7,16 +7,18 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.dbo.DAO import dev.dres.data.model.admin.PlainPassword import dev.dres.data.model.admin.UserName +import dev.dres.mgmt.admin.UserManager import dev.dres.mgmt.admin.UserManager.getMatchingUser import dev.dres.run.audit.AuditLogEntry import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource +import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.plugin.openapi.annotations.* -class LoginHandler(private val audit: DAO) : RestHandler, PostRestHandler { +class LoginHandler(private val audit: DAO) : RestHandler, PostRestHandler { data class LoginRequest(var username: String, var password: String) @@ -26,11 +28,11 @@ class LoginHandler(private val audit: DAO) : RestHandler, PostRes tags = ["User"], requestBody = OpenApiRequestBody([OpenApiContent(LoginRequest::class)]), responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ]) - override fun doPost(ctx: Context) : SuccessStatus{ + override fun doPost(ctx: Context) : UserDetails{ val loginRequest = try { ctx.bodyAsClass(LoginRequest::class.java) @@ -46,7 +48,8 @@ class LoginHandler(private val audit: DAO) : RestHandler, PostRes AccessManager.setUserForSession(ctx.sessionId(), user) AuditLogger.login(loginRequest.username, ctx.sessionId(), LogEventSource.REST) - return SuccessStatus("Login of '${user.username}' successful!") + + return UserDetails.create(UserManager.get(username)!!, ctx) } From afd85522730e64a0aee4e8724709476683004bee Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 21 Dec 2020 16:46:48 +0100 Subject: [PATCH 02/95] Added session param to some endpoints relevant for the client --- .../src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt | 6 ++++++ .../main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt | 4 ++++ .../kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt | 3 ++- .../main/kotlin/dev/dres/api/rest/handler/UserHandler.kt | 3 +++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt index 2b5b5b567..6a7208f25 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt @@ -48,6 +48,9 @@ class QueryLogHandler : LogHandler() { method = HttpMethod.POST, requestBody = OpenApiRequestBody([OpenApiContent(QueryEventLog::class)]), tags = ["Log"], + queryParams = [ + OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + ], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -82,6 +85,9 @@ class ResultLogHandler : LogHandler() { method = HttpMethod.POST, requestBody = OpenApiRequestBody([OpenApiContent(QueryResultLog::class)]), tags = ["Log"], + queryParams = [ + OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + ], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt index e715c703e..3bcbdf68d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt @@ -11,12 +11,16 @@ import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context import io.javalin.plugin.openapi.annotations.OpenApi import io.javalin.plugin.openapi.annotations.OpenApiContent +import io.javalin.plugin.openapi.annotations.OpenApiParam import io.javalin.plugin.openapi.annotations.OpenApiResponse class LogoutHandler(private val audit: DAO) : RestHandler, GetRestHandler { @OpenApi(summary = "Clears all user roles of the current session.", path = "/api/logout", tags = ["User"], + queryParams = [ + OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + ], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index 79f21ac0a..724dc4881 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -115,7 +115,8 @@ class SubmissionHandler (val collections: DAO, private val item OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), OpenApiParam(PARAMETER_NAME_FRAME, Int::class, "Frame number for media with temporal progression (e.g. video)."), OpenApiParam(PARAMETER_NAME_SHOT, Int::class, "Shot number for media with temporal progression (e.g. video)."), - OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g. video).") + OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g. video)."), + OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) ], tags = ["Submission"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt index 8323bbfa1..312b88277 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt @@ -224,6 +224,9 @@ class CurrentUsersSessionIdHandler : UserHandler(), GetRestHandler, A summary = "Get current sessionId", path = "/api/user/session", tags = ["User"], + queryParams = [ + OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + ], responses = [ OpenApiResponse("200", [OpenApiContent(SessionId::class)]), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) From 1507b199a0c1c2e36c85b94658cf216f8440236e Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 1 Feb 2021 08:59:17 +0100 Subject: [PATCH 03/95] #204 First preparation steps towards video player for query hints --- ...etition-builder-task-dialog.component.html | 3 ++ ...mpetition-builder-task-dialog.component.ts | 12 +++-- ...ayer-segment-builder-dialog.component.html | 5 +++ ...ayer-segment-builder-dialog.component.scss | 0 ...player-segment-builder-dialog.component.ts | 44 +++++++++++++++++++ ...ideo-player-segment-builder.component.html | 6 +-- .../video-player-segment-builder.component.ts | 16 ++++--- .../competition-builder.module.ts | 4 +- 8 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.html create mode 100644 frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.scss create mode 100644 frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.ts diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html index bfbb6a4a6..b6496a47d 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html @@ -216,6 +216,9 @@

Target +
+ +

Query description + +

diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.scss b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.ts new file mode 100644 index 000000000..ba94a0244 --- /dev/null +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.ts @@ -0,0 +1,44 @@ +import {Component, Inject, OnInit, ViewChild} from '@angular/core'; +import {AppConfig} from '../../../../app.config'; +import { + VideoPlayerSegmentBuilderComponent, + VideoPlayerSegmentBuilderData +} from '../video-player-segment-builder/video-player-segment-builder.component'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; + +@Component({ + selector: 'app-video-player-segment-builder-dialog', + templateUrl: './video-player-segment-builder-dialog.component.html', + styleUrls: ['./video-player-segment-builder-dialog.component.scss'] +}) +export class VideoPlayerSegmentBuilderDialogComponent implements OnInit { + + @ViewChild(VideoPlayerSegmentBuilderComponent) videoPlayer: VideoPlayerSegmentBuilderComponent; + + constructor(public config: AppConfig, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: VideoPlayerSegmentBuilderData) { + + } + + ngOnInit(): void { + } + + /** + * Fetches the data from the form, returns it to the dialog openeer and cloeses this dialog + */ + save(): void { + console.log('save'); + this.dialogRef.close(this.videoPlayer.fetchData()); + } + + /** + * Closes this dialog without saving + */ + close(): void { + console.log('close'); + this.dialogRef.close(null); + } + + +} diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html index 6c2c591f0..e9047136c 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html @@ -67,8 +67,4 @@

Segment Editor for {{data?.mediaItem?.name}}

-
- - - -
+ diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts index 489482ddf..5dc6e9da9 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts @@ -1,4 +1,4 @@ -import {AfterViewInit, Component, ElementRef, Inject, OnDestroy, ViewChild} from '@angular/core'; +import {AfterViewInit, Component, ElementRef, Inject, Input, OnDestroy, ViewChild} from '@angular/core'; import {Observable, of, Subscription} from 'rxjs'; import {RestMediaItem, TemporalRange} from '../../../../../../openapi'; import {AppConfig} from '../../../../app.config'; @@ -19,6 +19,8 @@ export interface VideoPlayerSegmentBuilderData { export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDestroy { + @Input() data: VideoPlayerSegmentBuilderData; + @ViewChild('videoPlayer', {static: false}) video: ElementRef; videoUrl: Observable; playtimeRelative: Observable; @@ -38,9 +40,9 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest private requestSub: Subscription; - constructor(public config: AppConfig, + constructor(public config: AppConfig/*, public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: VideoPlayerSegmentBuilderData) { + @Inject(MAT_DIALOG_DATA) public data: VideoPlayerSegmentBuilderData*/) { } @@ -132,14 +134,16 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest * Fetches the data from the form, returns it to the dialog openeer and cloeses this dialog */ save(): void { - this.dialogRef.close(this.fetchData()); + console.log("save"); + // this.dialogRef.close(this.fetchData()); } /** * Closes this dialog without saving */ close(): void { - this.dialogRef.close(null); + console.log("close"); + // this.dialogRef.close(null); } /** @@ -168,7 +172,7 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest this.recalcVideoTime(null); } - private fetchData() { + public fetchData() { const out = { start: {value: this.startInSeconds, unit: 'SECONDS'}, end: {value: this.endInSeconds, unit: 'SECONDS'} diff --git a/frontend/src/app/competition/competition-builder/competition-builder.module.ts b/frontend/src/app/competition/competition-builder/competition-builder.module.ts index 7776b48db..23606f211 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.module.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.module.ts @@ -32,6 +32,7 @@ import {MatButtonToggleModule} from '@angular/material/button-toggle'; import {MatGridListModule} from '@angular/material/grid-list'; import {AdvancedBuilderDialogComponent} from './competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component'; import {SharedModule} from '../../shared/shared.module'; +import { VideoPlayerSegmentBuilderDialogComponent } from './competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component'; @NgModule({ imports: [ @@ -71,7 +72,8 @@ import {SharedModule} from '../../shared/shared.module'; CompetitionBuilderTaskGroupDialogComponent, CompetitionBuilderTaskTypeDialogComponent, VideoPlayerSegmentBuilderComponent, - AdvancedBuilderDialogComponent + AdvancedBuilderDialogComponent, + VideoPlayerSegmentBuilderDialogComponent ], providers: [] }) From d6dd49933e7c917293c0cf58ccc39e3dcbbc306d Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 1 Feb 2021 11:39:33 +0100 Subject: [PATCH 04/95] Closes #204 Added video player --- ...etition-builder-task-dialog.component.html | 7 +-- ...mpetition-builder-task-dialog.component.ts | 45 +++++++++++-------- .../video-player-segment-builder.component.ts | 24 +++------- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html index b6496a47d..340c1a468 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html @@ -205,6 +205,9 @@

Target

+
+ +
@@ -216,9 +219,7 @@

Target

-
- -
+

Query description + + diff --git a/frontend/src/app/run/viewer-run-list.component.html b/frontend/src/app/run/viewer-run-list.component.html index 8b5496aa8..2d436be8d 100644 --- a/frontend/src/app/run/viewer-run-list.component.html +++ b/frontend/src/app/run/viewer-run-list.component.html @@ -18,6 +18,11 @@ + + + diff --git a/frontend/src/app/services/session/authentication.sevice.ts b/frontend/src/app/services/session/authentication.sevice.ts index a6f3a9454..ee0b66947 100644 --- a/frontend/src/app/services/session/authentication.sevice.ts +++ b/frontend/src/app/services/session/authentication.sevice.ts @@ -18,7 +18,7 @@ export class AuthenticationService { * Constructor */ constructor(@Inject(UserService) private userService: UserService) { - this.userService.getApiUserSession().pipe( + this.userService.getApiUserSession('').pipe( catchError(e => of(null)), filter(s => s != null), flatMap(s => this.userService.getApiUser()), @@ -61,7 +61,7 @@ export class AuthenticationService { * Tries to logout the current user. Returns an Observable! */ public logout() { - return this.userService.getApiLogout().pipe( + return this.userService.getApiLogout('').pipe( catchError(e => of(null)), tap(() => { this.userDetails.next(null); diff --git a/frontend/src/app/user/profile/profile.component.ts b/frontend/src/app/user/profile/profile.component.ts index 82efff430..181b62a61 100644 --- a/frontend/src/app/user/profile/profile.component.ts +++ b/frontend/src/app/user/profile/profile.component.ts @@ -36,7 +36,7 @@ export class ProfileComponent implements OnInit, OnDestroy { ) { this.user = this.authenticationService.user; this.loggedIn = this.authenticationService.isLoggedIn; - this.sessionId = this.userService.getApiUserSession(); + this.sessionId = this.userService.getApiUserSession(''); } ngOnInit(): void { From 89d79f84318facd3ea65bd41c986f0a5a0084f7e Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 1 Mar 2021 17:15:31 +0100 Subject: [PATCH 32/95] Introduced RunActionContext and refactored calls in InteractiveRunManager in order to work in both the synchronous and asynchronous case --- .../dev/dres/api/cli/CompetitionRunCommand.kt | 7 +- .../kotlin/dev/dres/api/rest/AccessManager.kt | 4 +- .../rest/handler/BatchSubmissionHandler.kt | 10 +- .../handler/CompetitionRunAdminHandler.kt | 96 +++++++++----- .../CompetitionRunClientInfoHandler.kt | 5 +- .../api/rest/handler/CompetitionRunHandler.kt | 39 ++++-- .../handler/CompetitionRunScoreHandler.kt | 21 +-- .../api/rest/handler/SubmissionHandler.kt | 13 +- .../dev/dres/api/rest/types/run/RunState.kt | 3 +- .../kotlin/dev/dres/data/model/admin/Role.kt | 14 +- .../dres/data/model/run/RunActionContext.kt | 34 +++++ .../run/InteractiveAsynchronousRunManager.kt | 125 ++++++++++++++++++ .../dev/dres/run/InteractiveRunManager.kt | 24 ++-- ...kt => InteractiveSynchronousRunManager.kt} | 95 +++++++------ .../dev/dres/run/NonInteractiveRunManager.kt | 12 -- ...=> NonInteractiveSynchronousRunManager.kt} | 27 ++-- .../main/kotlin/dev/dres/run/RunExecutor.kt | 4 +- .../main/kotlin/dev/dres/run/RunManager.kt | 9 +- .../dres/run/updatables/EndTaskUpdatable.kt | 7 +- 19 files changed, 395 insertions(+), 154 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt rename backend/src/main/kotlin/dev/dres/run/{SynchronousInteractiveRunManager.kt => InteractiveSynchronousRunManager.kt} (87%) delete mode 100644 backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt rename backend/src/main/kotlin/dev/dres/run/{SynchronousNonInteractiveRunManager.kt => NonInteractiveSynchronousRunManager.kt} (82%) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt index 872b2a8ad..327102f09 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt @@ -11,6 +11,7 @@ import dev.dres.data.dbo.DAO import dev.dres.data.model.UID import dev.dres.data.model.run.CompetitionRun import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.SubmissionStatus import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor @@ -51,7 +52,8 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktC } if (plain) { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - println("${RunSummary(it.id.string, it.name, it.competitionDescription.description, it.currentTask?.name)} (${it.status})") + println("${RunSummary(it.id.string, it.name, it.competitionDescription.description, it.currentTask( + RunActionContext.DUMMY_ADMIN)?.name)} (${it.status})") } } else { table { @@ -65,7 +67,8 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktC } body { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - row(it.id.string, it.name, it.competitionDescription.description, it.currentTask?.name + row(it.id.string, it.name, it.competitionDescription.description, it.currentTask( + RunActionContext.DUMMY_ADMIN)?.name ?: "N/A", it.status) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 29cd809e6..1bd69e84d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -21,7 +21,7 @@ object AccessManager { } } - private val sessionRoleMap = ConcurrentHashMap>() + private val sessionRoleMap = ConcurrentHashMap>() private val sessionUserMap = ConcurrentHashMap() /** Map keeping track of all [RunManager]s a specific user is eligible for. */ @@ -54,7 +54,7 @@ object AccessManager { sessionUserMap.remove(sessionId) } - fun rolesOfSession(sessionId: String): Set = sessionRoleMap[sessionId] ?: emptySet() + fun rolesOfSession(sessionId: String): Set = sessionRoleMap[sessionId] ?: emptySet() fun getUserIdForSession(sessionId: String): UID? = sessionUserMap[sessionId] diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt index 6ab98a183..35bd04ce0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt @@ -12,7 +12,7 @@ import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.basics.media.MediaItemSegmentList import dev.dres.data.model.run.* -import dev.dres.run.NonInteractiveRunManager +import dev.dres.run.NonInteractiveSynchronousRunManager import dev.dres.utilities.TimeUtil import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId @@ -30,8 +30,8 @@ abstract class BatchSubmissionHandler(internal val collections: DAO().find { it.id == runId } + internal fun getRelevantManager(userId: UID, runId: UID): NonInteractiveSynchronousRunManager? = AccessManager.getRunManagerForUser(userId) + .filterIsInstance().find { it.id == runId } } @@ -65,6 +65,8 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D val runManager = getRelevantManager(userId, runId) ?: throw ErrorStatusException(404, "Run ${runId.string} not found", ctx) + val rac = RunActionContext.runActionContext(ctx, runManager) + val jsonBatch = try{ ctx.body() } catch (e: Exception) { @@ -77,7 +79,7 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D val resultBatches = jsonBatch.batches.mapNotNull { batch -> - val task = runManager.tasks().find { it.taskDescription.name == batch.taskName } ?: return@mapNotNull null + val task = runManager.tasks(rac).find { it.taskDescription.name == batch.taskName } ?: return@mapNotNull null val mediaCollectionId = task.taskDescription.mediaCollectionId val results = batch.results.map { result -> diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index 45f5686b3..c008c5321 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -14,6 +14,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.* import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource @@ -30,9 +31,7 @@ import org.slf4j.LoggerFactory import java.io.File -abstract class AbstractCompetitionRunAdminRestHandler : RestHandler, AccessManagedRestHandler { - - override val permittedRoles: Set = setOf(RestApiRole.ADMIN) +abstract class AbstractCompetitionRunAdminRestHandler(override val permittedRoles: Set = setOf(RestApiRole.ADMIN, RestApiRole.PARTICIPANT)) : RestHandler, AccessManagedRestHandler { fun getRun(runId: UID): InteractiveRunManager? { val run = RunExecutor.managerForId(runId) @@ -45,12 +44,13 @@ abstract class AbstractCompetitionRunAdminRestHandler : RestHandler, AccessManag fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(404, "Parameter 'runId' is missing!'", ctx) }.UID() + } /** * REST handler to create a [InteractiveSynchronousCompetitionRun]. */ -class CreateCompetitionRunAdminHandler(private val competitions: DAO, private val collections: DAO, config: Config) : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { +class CreateCompetitionRunAdminHandler(private val competitions: DAO, private val collections: DAO, config: Config) : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { private val cacheLocation = File(config.cachePath + "/tasks") private val logger = LoggerFactory.getLogger(this.javaClass) @@ -84,7 +84,7 @@ class CreateCompetitionRunAdminHandler(private val competitions: DAO TODO() - RunType.SYNCHRONOUS -> SynchronousInteractiveRunManager(competitionToStart, competitionStartMessage.name) + RunType.SYNCHRONOUS -> InteractiveSynchronousRunManager(competitionToStart, competitionStartMessage.name) } /**... and schedule RunManager. */ @@ -151,12 +151,17 @@ class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler() override fun doPost(ctx: Context): SuccessStatus { val runId = runId(ctx) val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) + + val rac = runActionContext(ctx, run) + try { - run.start() + run.start(rac) AuditLogger.competitionStart(run.id, LogEventSource.REST, ctx.sessionId()) return SuccessStatus("Run $runId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Run $runId could not be started because it is in the wrong state (state = ${run.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } } } @@ -182,14 +187,19 @@ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandle override fun doPost(ctx: Context): SuccessStatus { val runId = runId(ctx) val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) + + val rac = runActionContext(ctx, run) + try { - if (run.nextTask()) { - return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask!!.name}'.") + if (run.nextTask(rac)) { + return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask(rac)!!.name}'.") } else { throw ErrorStatusException(400, "Task for run $runId could not be changed because there are no tasks left.", ctx) } } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task for run $runId could not be changed because run is in the wrong state (state = ${run.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } } } @@ -222,13 +232,17 @@ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHand throw ErrorStatusException(404, "Parameter 'idx' is missing!'", ctx) }.toInt() + val rac = runActionContext(ctx, run) + try { - run.goToTask(idx) - return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask!!.name}'.") + run.goToTask(rac, idx) + return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask(rac)!!.name}'.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task for run $runId could not be changed because run is in the wrong state (state = ${run.status}).", ctx) } catch (e: IndexOutOfBoundsException) { throw ErrorStatusException(404, "Task for run $runId could not be changed because index $idx is out of bounds for number of available tasks.", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } } } @@ -236,7 +250,7 @@ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHand /** * REST handler to move to the previous task in a [InteractiveSynchronousCompetitionRun]. */ -class PreviousTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { +class PreviousTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/:runId/task/previous" @OpenApi( @@ -254,14 +268,17 @@ class PreviousTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHa override fun doPost(ctx: Context): SuccessStatus { val runId = runId(ctx) val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) + val rac = runActionContext(ctx, run) try { - if (run.previousTask()) { - return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask!!.name}'.") + if (run.previousTask(rac)) { + return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask(rac)!!.name}'.") } else { throw ErrorStatusException(400, "Task for run $runId could not be changed because there are no tasks left.", ctx) } } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task for run $runId could not be changed because run is in the wrong state (state = ${run.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } } } @@ -287,13 +304,16 @@ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl override fun doPost(ctx: Context): SuccessStatus { val runId = runId(ctx) val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) + val rac = runActionContext(ctx, run) try { - run.startTask() - AuditLogger.taskStart(run.id, run.currentTask?.name ?: "n/a", LogEventSource.REST, ctx.sessionId()) - EventStreamProcessor.event(TaskStartEvent(runId, run.currentTaskRun!!.uid, run.currentTask!!)) - return SuccessStatus("Task '${run.currentTask!!.name}' for run $runId was successfully started.") + run.startTask(rac) + AuditLogger.taskStart(run.id, run.currentTask(rac)?.name ?: "n/a", LogEventSource.REST, ctx.sessionId()) + EventStreamProcessor.event(TaskStartEvent(runId, run.currentTaskRun!!.uid, run.currentTask(rac)!!)) + return SuccessStatus("Task '${run.currentTask(rac)!!.name}' for run $runId was successfully started.") } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task '${run.currentTask!!.name}' for run $runId could not be started because run is in the wrong state (state = ${run.status}).", ctx) + throw ErrorStatusException(400, "Task '${run.currentTask(rac)!!.name}' for run $runId could not be started because run is in the wrong state (state = ${run.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } } } @@ -319,13 +339,16 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl override fun doPost(ctx: Context): SuccessStatus { val runId = runId(ctx) val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) + val rac = runActionContext(ctx, run) try { - val task = run.currentTask - run.abortTask() + val task = run.currentTask(rac) + run.abortTask(rac) AuditLogger.taskEnd(run.id, task?.name ?: "n/a", LogEventSource.REST, ctx.sessionId()) - return SuccessStatus("Task '${run.currentTask!!.name}' for run $runId was successfully aborted.") + return SuccessStatus("Task '${run.currentTask(rac)!!.name}' for run $runId was successfully aborted.") } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task '${run.currentTask!!.name}' for run $runId could not be aborted because run is in the wrong state (state = ${run.status}).", ctx) + throw ErrorStatusException(400, "Task '${run.currentTask(rac)!!.name}' for run $runId could not be aborted because run is in the wrong state (state = ${run.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } } } @@ -333,7 +356,7 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl /** * REST handler to terminate a [InteractiveSynchronousCompetitionRun]. */ -class TerminateCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { +class TerminateCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/:runId/terminate" @OpenApi( @@ -351,12 +374,15 @@ class TerminateCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl override fun doPost(ctx: Context): SuccessStatus { val runId = runId(ctx) val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) + val rac = runActionContext(ctx, run) try { - run.end() + run.end(rac) AuditLogger.competitionEnd(run.id, LogEventSource.REST, ctx.sessionId()) return SuccessStatus("Run $runId was successfully terminated.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Run $runId could not be terminated because it is in the wrong state (state = ${run.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } } } @@ -364,7 +390,7 @@ class TerminateCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl /** * REST handler to adjust a [InteractiveSynchronousCompetitionRun.TaskRun]'s duration. */ -class AdjustDurationRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { +class AdjustDurationRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/:runId/adjust/:duration" @OpenApi( @@ -389,20 +415,23 @@ class AdjustDurationRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), val duration = ctx.pathParamMap().getOrElse("duration") { throw ErrorStatusException(404, "Parameter 'duration' is missing!'", ctx) }.toInt() + val rac = runActionContext(ctx, run) try { - run.adjustDuration(duration) - AuditLogger.taskModified(run.id, run.currentTask?.name + run.adjustDuration(rac, duration) + AuditLogger.taskModified(run.id, run.currentTask(rac)?.name ?: "n/a", "Task duration adjusted by ${duration}s.", LogEventSource.REST, ctx.sessionId()) return SuccessStatus("Duration for run $runId was successfully adjusted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Duration for run $runId could not be adjusted because it is in the wrong state (state = ${run.status}).", ctx) } catch (e: IllegalArgumentException) { throw ErrorStatusException(400, "Duration for run $runId could not be adjusted because new duration would drop bellow zero (state = ${run.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } } } -class ListSubmissionsPerTaskRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), GetRestHandler> { +class ListSubmissionsPerTaskRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), GetRestHandler> { override val route: String = "run/admin/:runId/submissions/list/:taskId" @OpenApi( @@ -432,7 +461,7 @@ class ListSubmissionsPerTaskRunAdminHandler : AbstractCompetitionRunAdminRestHan } } -class OverrideSubmissionStatusRunAdminHandler: AbstractCompetitionRunAdminRestHandler(), PatchRestHandler{ +class OverrideSubmissionStatusRunAdminHandler: AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PatchRestHandler{ override val route: String = "run/admin/:runId/submissions/override" @OpenApi( @@ -472,7 +501,7 @@ class OverrideSubmissionStatusRunAdminHandler: AbstractCompetitionRunAdminRestHa /** * REST handler to list all viewers for a [InteractiveSynchronousCompetitionRun]. */ -class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), GetRestHandler> { +class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), GetRestHandler> { override val route: String = "run/admin/:runId/viewer/list" @OpenApi( @@ -499,7 +528,7 @@ class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), Get /** * REST handler to force the viewer state of a viewer instance registered for a [RunManager]. */ -class ForceViewerRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { +class ForceViewerRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/:runId/viewers/:viewerId/force" @OpenApi( @@ -524,8 +553,9 @@ class ForceViewerRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), Pos throw ErrorStatusException(404, "Parameter 'viewerId' is missing!'", ctx) } val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) + val rac = runActionContext(ctx, run) try { - if (run.overrideReadyState(viewerId)) { + if (run.overrideReadyState(rac, viewerId)) { return SuccessStatus("State for viewer $viewerId forced successfully.") } else { throw ErrorStatusException(404, "Viewer $viewerId does not exist!'", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt index b04d84592..31eea593b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.RestApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.UID +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManager @@ -119,6 +120,8 @@ class CompetitionRunClientCurrentTaskInfoHandler : AbstractCompetitionRunClientI val task = run.currentTaskRun ?: throw ErrorStatusException(404, "Specified run has no active task", ctx) + val rac = runActionContext(ctx, run) + return ClientTaskInfo( task.uid.string, task.taskDescription.name, @@ -127,7 +130,7 @@ class CompetitionRunClientCurrentTaskInfoHandler : AbstractCompetitionRunClientI RunManagerStatus.CREATED -> 0 RunManagerStatus.ACTIVE, RunManagerStatus.PREPARING_TASK -> task.duration - RunManagerStatus.RUNNING_TASK -> run.timeLeft() / 1000 + RunManagerStatus.RUNNING_TASK -> run.timeLeft(rac) / 1000 RunManagerStatus.TASK_ENDED -> 0 RunManagerStatus.TERMINATED -> 0 }, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index 6dce28eb5..e3ab6c1ed 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -16,6 +16,7 @@ import dev.dres.data.model.Config import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManagerStatus @@ -70,6 +71,7 @@ abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRes fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() + } class ListCompetitionRunInfosHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { @@ -101,7 +103,12 @@ class ListCompetitionRunStatesHandler : AbstractCompetitionRunRestHandler(), Get OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): List = getRelevantManagers(ctx).map { RunState(it) } + override fun doGet(ctx: Context): List = + getRelevantManagers(ctx).map { + val rac = runActionContext(ctx, it) + RunState(it, rac) + } + } @@ -158,7 +165,9 @@ class GetCompetitionRunStateHandler : AbstractCompetitionRunRestHandler(), GetRe throw ErrorStatusException(403, "Access Denied", ctx) } - return RunState(run) + val rac = runActionContext(ctx, run) + + return RunState(run, rac) } } @@ -183,11 +192,13 @@ class CurrentTaskInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandl val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) + val rac = runActionContext(ctx, run) + if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied.", ctx) } - return TaskInfo(run.currentTask ?: throw ErrorStatusException(404, "Run $runId has currently no active task.", ctx)) + return TaskInfo(run.currentTask(rac) ?: throw ErrorStatusException(404, "Run $runId has currently no active task.", ctx)) } } @@ -215,7 +226,9 @@ class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRu throw ErrorStatusException(403, "Access denied.", ctx) } - val task = run.currentTask ?: throw ErrorStatusException(404, "No active task in run $runId.", ctx) + val rac = runActionContext(ctx, run) + + val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "No active task in run $runId.", ctx) try { return task.toTaskHint(config) } catch (e: FileNotFoundException) { @@ -256,8 +269,10 @@ class CurrentTaskTargetHandler(private val config: Config, private val collectio throw ErrorStatusException(400, "Query target can only be loaded if task has just ended.", ctx) } + val rac = runActionContext(ctx, run) + /* Fetch query target and transform it. */ - val task = run.currentTask ?: throw ErrorStatusException(404, "No active task in run $runId.", ctx) + val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "No active task in run $runId.", ctx) try { val target = task.toTaskTarget(config, collections) if (target != null) { @@ -297,9 +312,11 @@ class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandle throw ErrorStatusException(403, "Access denied.", ctx) } + val rac = runActionContext(ctx, run) + /* Obtain current task run and check status. */ return if (run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTask?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + if (run.currentTask(rac)?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { run.submissions.map { SubmissionInfo.blind(it) } } else { run.submissions.map { SubmissionInfo.withId(it) } @@ -335,9 +352,11 @@ class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRest throw ErrorStatusException(403, "Access denied", ctx) } + val rac = runActionContext(ctx, run) + val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() return if (run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTask?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + if (run.currentTask(rac)?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { run.submissions.filter { it.timestamp >= timestamp }.map { SubmissionInfo.blind(it) } } else { run.submissions.filter { it.timestamp >= timestamp }.map { SubmissionInfo.withId(it) } @@ -375,15 +394,17 @@ class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRes throw ErrorStatusException(403, "Access denied", ctx) } + val taskId = ctx.pathParamMap()["taskId"]?.UID() ?: throw ErrorStatusException(404, "Missing task id", ctx) + val rac = runActionContext(ctx, run) return if (run.currentTaskRun?.taskDescriptionId == taskId && run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTask?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + if (run.currentTask(rac)?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { run.submissions.map { SubmissionInfo.blind(it) } } else { run.submissions.map { SubmissionInfo.withId(it) } } } else { - run.taskRunForId(taskId)?.submissions?.map { SubmissionInfo(it) } ?: emptyList() + run.taskRunForId(rac, taskId)?.submissions?.map { SubmissionInfo(it) } ?: emptyList() } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index 018443c20..a64d06fea 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.RestApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.UID +import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.score.scoreboard.Score @@ -38,12 +39,6 @@ abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler */ fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN) - /** - * Checks if the current session has the [RestApiRole.ADMIN]. - * - * @param ctx The [Context] to check. - */ - fun isAdmin(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN) fun getRun(ctx: Context, runId: UID): InteractiveRunManager? { if (isParticipant(ctx)) { @@ -120,9 +115,11 @@ class CurrentTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler Score(team.uid.string, scores[team.uid] ?: 0.0) } @@ -161,14 +158,18 @@ class HistoryTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler, private val item it.users.contains(userId) }?.uid ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) + val rac = RunActionContext.runActionContext(ctx, runManager) + /* Find collectionId the submission belongs to.. */ val collectionParam = map[PARAMETER_NAME_COLLECTION]?.first() val collectionId: UID = when { collectionParam != null -> this.collections.find { it.name == collectionParam }?.id - else -> runManager.currentTask?.mediaCollectionId + else -> runManager.currentTask(rac)?.mediaCollectionId } ?: throw ErrorStatusException(404, "Media collection '$collectionParam' could not be found.", ctx) /* Find media item. */ @@ -83,7 +85,7 @@ class SubmissionHandler (val collections: DAO, private val item val item = this.itemIndex[collectionId to itemParam].firstOrNull() ?: throw ErrorStatusException(404, "Media item '$itemParam (collection = $collectionId)' could not be found.", ctx) - val mapToSegment = runManager.currentTask?.taskType?.options?.any { it.option == TaskType.Options.MAP_TO_SEGMENT } == true + val mapToSegment = runManager.currentTask(rac)?.taskType?.options?.any { it.option == TaskType.Options.MAP_TO_SEGMENT } == true return when { map.containsKey(PARAMETER_NAME_SHOT) && item is MediaItem.VideoItem -> { @@ -140,14 +142,15 @@ class SubmissionHandler (val collections: DAO, private val item throw ErrorStatusException(208, "Submission rejected", ctx) } - AuditLogger.submission(run.id, run.currentTask?.name ?: "no task", submission, LogEventSource.REST, ctx.sessionId(), ctx.req.remoteAddr) + val rac = RunActionContext.runActionContext(ctx, run) + + AuditLogger.submission(run.id, run.currentTask(rac)?.name ?: "no task", submission, LogEventSource.REST, ctx.sessionId(), ctx.req.remoteAddr) EventStreamProcessor.event(SubmissionEvent(ctx.sessionId(), run.id, run.currentTaskRun?.uid, submission)) - if (run.currentTask?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { //pre-generate preview + if (run.currentTask(rac)?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { //pre-generate preview generatePreview(submission) } - return when (result) { SubmissionStatus.CORRECT -> SuccessStatus("Submission correct!") SubmissionStatus.WRONG -> SuccessStatus("Submission incorrect! Try again") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt index 30fc2ae32..8acf0f1ac 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt @@ -1,6 +1,7 @@ package dev.dres.api.rest.types.run import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus @@ -14,6 +15,6 @@ import dev.dres.run.RunManagerStatus * @version 1.0.2 */ data class RunState(val id: String, val status: RunManagerStatus, val currentTask: TaskInfo?, val timeLeft: Long) { - constructor(run: InteractiveRunManager) : this(run.id.string, run.status, run.currentTask?.let { TaskInfo(it) }, run.timeLeft() / 1000) + constructor(run: InteractiveRunManager, context: RunActionContext) : this(run.id.string, run.status, run.currentTask(context)?.let { TaskInfo(it) }, run.timeLeft(context) / 1000) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt index ecd9ca5e5..3eef71b5c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt @@ -1,8 +1,20 @@ package dev.dres.data.model.admin +import dev.dres.api.rest.RestApiRole + /** * Roles currently supported by DRES. */ enum class Role { - ADMIN, JUDGE, VIEWER, PARTICIPANT + ADMIN, JUDGE, VIEWER, PARTICIPANT; + + companion object { + fun fromRestRole(role: RestApiRole): Role = when(role) { + RestApiRole.ANYONE, + RestApiRole.VIEWER -> VIEWER + RestApiRole.PARTICIPANT -> PARTICIPANT + RestApiRole.JUDGE -> JUDGE + RestApiRole.ADMIN -> ADMIN + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt new file mode 100644 index 000000000..5556fade4 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -0,0 +1,34 @@ +package dev.dres.data.model.run + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.UID +import dev.dres.data.model.admin.Role +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.competition.TeamId +import dev.dres.run.RunManager +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context + +data class RunActionContext( + val userId: UserId, + val teamId: TeamId?, + val roles: Set +) { + val isAdmin: Boolean + get() = roles.contains(Role.ADMIN) + + companion object { + fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { + val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user", ctx) + val roles = AccessManager.rolesOfSession(ctx.sessionId()).map { Role.fromRestRole(it) }.toSet() + val teamId = runManager.competitionDescription.teams.find { it.users.contains(userId) }?.uid + + return RunActionContext(userId, teamId, roles) + } + + val DUMMY_ADMIN = RunActionContext(UID.EMPTY, UID.EMPTY, setOf(Role.ADMIN)) + + } + +} diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt new file mode 100644 index 000000000..5655b792a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -0,0 +1,125 @@ +package dev.dres.run + +import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.types.run.websocket.ClientMessage +import dev.dres.data.model.UID +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.run.* +import dev.dres.run.score.ScoreTimePoint +import dev.dres.run.score.scoreboard.Scoreboard +import dev.dres.run.validation.interfaces.JudgementValidator + +class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronousCompetitionRun) : InteractiveRunManager { + + + + override fun currentTask(context: RunActionContext): TaskDescription? { + TODO("Not yet implemented") + } + + override val submissions: List + get() = TODO("Not yet implemented") + + override val scoreHistory: List + get() = TODO("Not yet implemented") + + override val allSubmissions: List + get() = TODO("Not yet implemented") + + override val currentTaskRun: InteractiveSynchronousCompetitionRun.TaskRun? + get() = TODO("Not yet implemented") + + override fun tasks(context: RunActionContext): List { + TODO("Not yet implemented") + } + + override fun previousTask(context: RunActionContext): Boolean { + TODO("Not yet implemented") + } + + override fun nextTask(context: RunActionContext): Boolean { + TODO("Not yet implemented") + } + + override fun goToTask(context: RunActionContext, index: Int) { + TODO("Not yet implemented") + } + + override fun startTask(context: RunActionContext) { + TODO("Not yet implemented") + } + + override fun abortTask(context: RunActionContext) { + TODO("Not yet implemented") + } + + override fun adjustDuration(context: RunActionContext, s: Int): Long { + TODO("Not yet implemented") + } + + override fun timeLeft(context: RunActionContext): Long { + TODO("Not yet implemented") + } + + override fun taskRunForId( + context: RunActionContext, + taskRunId: UID + ): InteractiveSynchronousCompetitionRun.TaskRun? { + TODO("Not yet implemented") + } + + override fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean { + TODO("Not yet implemented") + } + + override fun postSubmission(sub: Submission): SubmissionStatus { + TODO("Not yet implemented") + } + + override fun updateSubmission(suid: UID, newStatus: SubmissionStatus): Boolean { + TODO("Not yet implemented") + } + + override val id: UID + get() = TODO("Not yet implemented") + + override val name: String + get() = TODO("Not yet implemented") + + override val competitionDescription: CompetitionDescription + get() = this.run.competitionDescription + + override val scoreboards: List + get() = TODO("Not yet implemented") + + override val status: RunManagerStatus + get() = TODO("Not yet implemented") + + override val judgementValidators: List + get() = TODO("Not yet implemented") + + override fun start(context: RunActionContext) { + TODO("Not yet implemented") + } + + override fun end(context: RunActionContext) { + TODO("Not yet implemented") + } + + override fun taskCount(context: RunActionContext): Int { + TODO("Not yet implemented") + } + + override fun viewers(): Map { + TODO("Not yet implemented") + } + + override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean { + TODO("Not yet implemented") + } + + override fun run() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 7b0c04a2c..9de621bc1 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -14,14 +14,14 @@ interface InteractiveRunManager : RunManager { * * Part of the [RunManager]'s navigational state. */ - val currentTask: TaskDescription? + fun currentTask(context: RunActionContext): TaskDescription? /** * List of [Submission]s for the current [InteractiveSynchronousCompetitionRun.TaskRun]. * * Part of the [RunManager]'s execution state. Can be empty! */ - val submissions: List + val submissions: List //TODO needs to be changed to work with asynchronous runs /** List of [ScoreTimePoint]s tracking the states of the different [Scoreboard]s over time*/ val scoreHistory: List @@ -36,7 +36,7 @@ interface InteractiveRunManager : RunManager { */ val currentTaskRun: InteractiveSynchronousCompetitionRun.TaskRun? - override fun tasks(): List + override fun tasks(context: RunActionContext): List /** * Prepares this [RunManager] for the execution of previous [Task] as per order defined in [CompetitionDescription.tasks]. @@ -48,7 +48,7 @@ interface InteractiveRunManager : RunManager { * @return True if [Task] was moved, false otherwise. Usually happens if last [Task] has been reached. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ - fun previousTask(): Boolean + fun previousTask(context: RunActionContext): Boolean /** * Prepares this [RunManager] for the execution of next [Task] as per order defined in [CompetitionDescription.tasks]. @@ -60,7 +60,7 @@ interface InteractiveRunManager : RunManager { * @return True if [Task] was moved, false otherwise. Usually happens if last [Task] has been reached. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ - fun nextTask(): Boolean + fun nextTask(context: RunActionContext): Boolean /** * Prepares this [RunManager] for the execution of the [Task] given by the index as per order @@ -71,7 +71,7 @@ interface InteractiveRunManager : RunManager { * * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ - fun goToTask(index: Int) + fun goToTask(context: RunActionContext, index: Int) /** * Starts the [RunManager.currentTask] and thus moves the [RunManager.status] from @@ -82,7 +82,7 @@ interface InteractiveRunManager : RunManager { * * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] or [RunManager.currentTask] is not set. */ - fun startTask() + fun startTask(context: RunActionContext) /** * Force-abort the [RunManager.currentTask] and thus moves the [RunManager.status] from @@ -93,7 +93,7 @@ interface InteractiveRunManager : RunManager { * * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun abortTask() + fun abortTask(context: RunActionContext) /** * Adjusts the duration of the current [TaskRun] by the specified amount. Amount can be positive or negative. @@ -104,7 +104,7 @@ interface InteractiveRunManager : RunManager { * @throws IllegalArgumentException If the specified correction cannot be applied. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun adjustDuration(s: Int): Long + fun adjustDuration(context: RunActionContext, s: Int): Long /** * Returns the time in milliseconds that is left until the end of the currently running task. @@ -113,7 +113,7 @@ interface InteractiveRunManager : RunManager { * * @return Time remaining until the task will end or -1, if no task is running. */ - fun timeLeft(): Long + fun timeLeft(context: RunActionContext): Long /** * Returns [InteractiveSynchronousCompetitionRun.TaskRun]s for the specified index. The index is zero based, i.e., @@ -121,7 +121,7 @@ interface InteractiveRunManager : RunManager { * * @param taskRunId The [UID] of the desired [InteractiveSynchronousCompetitionRun.TaskRun]. */ - fun taskRunForId(taskRunId: UID): InteractiveSynchronousCompetitionRun.TaskRun? + fun taskRunForId(context: RunActionContext, taskRunId: UID): InteractiveSynchronousCompetitionRun.TaskRun? /** * Override the ready state for a given viewer ID. @@ -129,7 +129,7 @@ interface InteractiveRunManager : RunManager { * @param viewerId The ID of the viewer that should be overridden. * @return true on success, false otherwise */ - fun overrideReadyState(viewerId: String): Boolean + fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean /** * Invoked by an external caller to post a new [Submission] for the [Task] that is currently being diff --git a/backend/src/main/kotlin/dev/dres/run/SynchronousInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt similarity index 87% rename from backend/src/main/kotlin/dev/dres/run/SynchronousInteractiveRunManager.kt rename to backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index c79eaec2b..227152541 100644 --- a/backend/src/main/kotlin/dev/dres/run/SynchronousInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -9,10 +9,7 @@ import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun -import dev.dres.data.model.run.InteractiveTask -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.run.* import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.run.eventstream.EventStreamProcessor @@ -38,7 +35,7 @@ import kotlin.math.max * @version 2.1.0 * @author Ralph Gasser */ -class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitionRun) : InteractiveRunManager { +class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitionRun) : InteractiveRunManager { private val VIEWER_TIME_OUT = 30L //TODO make configurable @@ -56,21 +53,22 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio */ constructor(description: CompetitionDescription, name: String) : this(InteractiveSynchronousCompetitionRun(UID.EMPTY, name, description).apply { RunExecutor.runs.append(this) }) - /** Run ID of this [SynchronousInteractiveRunManager]. */ + /** Run ID of this [InteractiveSynchronousRunManager]. */ override val id: UID get() = this.run.id - /** Name of this [SynchronousInteractiveRunManager]. */ + /** Name of this [InteractiveSynchronousRunManager]. */ override val name: String get() = this.run.name - /** The [CompetitionDescription] executed by this [SynchronousInteractiveRunManager]. */ + /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ override val competitionDescription: CompetitionDescription get() = this.run.competitionDescription /** Reference to the currently active [TaskDescription]. This is part of the task navigation. */ - override var currentTask: TaskDescription = this.competitionDescription.tasks[0] - private set + private var currentTask = this.competitionDescription.tasks[0] + override fun currentTask(context: RunActionContext): TaskDescription = currentTask + /** Reference to the currently active [InteractiveSynchronousCompetitionRun.TaskRun].*/ override val currentTaskRun: InteractiveSynchronousCompetitionRun.TaskRun? @@ -83,7 +81,7 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio } } - override fun tasks(): List = this.run.tasks + override fun tasks(context: RunActionContext): List = this.run.tasks /** The list of [Submission]s for the current [InteractiveSynchronousCompetitionRun.TaskRun]. */ override val submissions: List @@ -91,7 +89,7 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio this.currentTaskRun?.submissions ?: emptyList() } - /** The list of all [Submission]s tracked ever received by this [SynchronousInteractiveRunManager]. */ + /** The list of all [Submission]s tracked ever received by this [InteractiveSynchronousRunManager]. */ override val allSubmissions: List get() = this.stateLock.read { this.run.tasks.flatMap { it.submissions } @@ -109,11 +107,11 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio } private set - /** Returns list [JudgementValidator]s associated with this [SynchronousInteractiveRunManager]. May be empty*/ + /** Returns list [JudgementValidator]s associated with this [InteractiveSynchronousRunManager]. May be empty*/ override val judgementValidators: List get() = this.run.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } - /** List of [Scoreboard]s for this [SynchronousInteractiveRunManager]. */ + /** List of [Scoreboard]s for this [InteractiveSynchronousRunManager]. */ override val scoreboards: List get() = this.scoreboardsUpdatable.scoreboards @@ -124,28 +122,33 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio /** Internal data structure that tracks all [WebSocketConnection]s and their ready state (for [RunManagerStatus.PREPARING_TASK]) */ private val readyLatch = ReadyLatch() - /** The internal [ScoreboardsUpdatable] instance for this [SynchronousInteractiveRunManager]. */ + /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoreboardsUpdatable = ScoreboardsUpdatable(this.competitionDescription.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) - /** The internal [MessageQueueUpdatable] instance used by this [SynchronousInteractiveRunManager]. */ + /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) - /** The internal [ScoresUpdatable] instance for this [SynchronousInteractiveRunManager]. */ + /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoresUpdatable = ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable) - /** The internal [DAOUpdatable] instance used by this [SynchronousInteractiveRunManager]. */ + /** The internal [DAOUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) /** The internal [DAOUpdatable] used to end a task once no more submissions are possible */ - private val endTaskUpdatable = EndTaskUpdatable(this) + private val endTaskUpdatable = EndTaskUpdatable(this, RunActionContext.DUMMY_ADMIN ) - /** List of [Updatable] held by this [SynchronousInteractiveRunManager]. */ + /** List of [Updatable] held by this [InteractiveSynchronousRunManager]. */ private val updatables = mutableListOf() - /** A lock for state changes to this [SynchronousInteractiveRunManager]. */ + /** A lock for state changes to this [InteractiveSynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() + private fun checkContext(context: RunActionContext) { + if (!context.isAdmin) + throw IllegalAccessError("functionality of SynchronousInteractiveRunManager only available to administrators") + } + init { /* Register relevant Updatables. */ this.updatables.add(this.scoresUpdatable) @@ -176,8 +179,9 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio this.scoresUpdatable.update(this.status) } - override fun start() = this.stateLock.write { + override fun start(context: RunActionContext) = this.stateLock.write { check(this.status == RunManagerStatus.CREATED) { "SynchronizedRunManager is in status ${this.status} and cannot be started." } + checkContext(context) /* Start the run. */ this.run.start() @@ -194,8 +198,9 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio LOGGER.info("SynchronousRunManager ${this.id} started") } - override fun end() = this.stateLock.write { + override fun end(context: RunActionContext) = this.stateLock.write { check(this.status != RunManagerStatus.TERMINATED) { "SynchronizedRunManager is in status ${this.status} and cannot be terminated." } + checkContext(context) /* End the run. */ this.run.end() @@ -212,7 +217,8 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio LOGGER.info("SynchronousRunManager ${this.id} terminated") } - override fun previousTask(): Boolean = this.stateLock.write { + override fun previousTask(context: RunActionContext): Boolean = this.stateLock.write { + checkContext(context) val newIndex = this.competitionDescription.tasks.indexOf(this.currentTask) - 1 return try { this.goToTask(newIndex) @@ -222,7 +228,8 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio } } - override fun nextTask(): Boolean = this.stateLock.write { + override fun nextTask(context: RunActionContext): Boolean = this.stateLock.write { + checkContext(context) val newIndex = this.competitionDescription.tasks.indexOf(this.currentTask) + 1 return try { this.goToTask(newIndex) @@ -232,7 +239,12 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio } } - override fun goToTask(index: Int) = this.stateLock.write { + override fun goToTask(context: RunActionContext, index: Int) { + checkContext(context) + goToTask(index) + } + + private fun goToTask(index: Int) = this.stateLock.write { check(this.status == RunManagerStatus.ACTIVE || this.status == RunManagerStatus.TASK_ENDED) { "SynchronizedRunManager is in status ${this.status}. Tasks can therefore not be changed." } if (index >= 0 && index < this.competitionDescription.tasks.size) { @@ -254,10 +266,12 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio } } - override fun startTask() = this.stateLock.write { + override fun startTask(context: RunActionContext) = this.stateLock.write { check(this.status == RunManagerStatus.ACTIVE || this.status == RunManagerStatus.TASK_ENDED) { "SynchronizedRunManager is in status ${this.status}. Tasks can therefore not be started." } + checkContext(context) + /* Create and prepare pipeline for submission. */ - this.run.newTaskRun(this.currentTask.id) + this.run.newTaskRun(this.currentTask(context).id) /* Create and prepare pipeline for submission (FIXME: is this used?). */ //val ret = this.run.newTaskRun(this.currentTask.id) @@ -280,7 +294,8 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio LOGGER.info("SynchronousRunManager ${this.id} started task task ${this.currentTask}") } - override fun abortTask() = this.stateLock.write { + override fun abortTask(context: RunActionContext) = this.stateLock.write { + checkContext(context) if (!(this.status == RunManagerStatus.PREPARING_TASK || this.status == RunManagerStatus.RUNNING_TASK)) { throw IllegalStateException("SynchronizedRunManager is in status ${this.status}. Tasks can therefore not be aborted.") } @@ -306,14 +321,14 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio * * @param taskRunId The [UID] of the [InteractiveSynchronousCompetitionRun.TaskRun]. */ - override fun taskRunForId(taskRunId: UID): InteractiveSynchronousCompetitionRun.TaskRun? = this.run.tasks.find { it.uid == taskRunId } + override fun taskRunForId(context: RunActionContext, taskRunId: UID): InteractiveSynchronousCompetitionRun.TaskRun? = this.run.tasks.find { it.uid == taskRunId } /** * Returns the number of [InteractiveSynchronousCompetitionRun.TaskRun]s held by this [RunManager]. * * @return The number of [InteractiveSynchronousCompetitionRun.TaskRun]s held by this [RunManager] */ - override fun taskCount(): Int = this.run.tasks.size + override fun taskCount(context: RunActionContext): Int = this.run.tasks.size /** * Adjusts the duration of the current [InteractiveSynchronousCompetitionRun.TaskRun] by the specified amount. Amount can be either positive or negative. @@ -324,7 +339,8 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio * @throws IllegalArgumentException If the specified correction cannot be applied. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - override fun adjustDuration(s: Int): Long = this.stateLock.read { + override fun adjustDuration(context: RunActionContext, s: Int): Long = this.stateLock.read { + checkContext(context) check(this.status == RunManagerStatus.RUNNING_TASK) { "SynchronizedRunManager is in status ${this.status}. Duration of task can therefore not be adjusted." } val currentTaskRun = this.currentTaskRun ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") @@ -341,7 +357,9 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio * * @return Time remaining until the task will end or -1, if no task is running. */ - override fun timeLeft(): Long = this.stateLock.read { + override fun timeLeft(context: RunActionContext): Long = timeLeft() + + fun timeLeft(): Long = this.stateLock.read { if (this.status == RunManagerStatus.RUNNING_TASK) { val currentTaskRun = this.currentTaskRun ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") return max(0L, currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) @@ -351,7 +369,7 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio } /** - * Lists all WebsSocket session IDs for viewer instances currently registered to this [SynchronousInteractiveRunManager]. + * Lists all WebsSocket session IDs for viewer instances currently registered to this [InteractiveSynchronousRunManager]. * * @return Map of session ID to ready state. */ @@ -362,7 +380,8 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio * * @param viewerId The ID of the viewer's WebSocket session. */ - override fun overrideReadyState(viewerId: String): Boolean = this.stateLock.read { + override fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean = this.stateLock.read { + checkContext(context) check(this.status == RunManagerStatus.PREPARING_TASK) { } return try { val viewer = this.readyLatch.state().keys.find { it.sessionId == viewerId } @@ -467,7 +486,7 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio var errorCounter = 0 - /** Start [SynchronousInteractiveRunManager] . */ + /** Start [InteractiveSynchronousRunManager] . */ while (this.status != RunManagerStatus.TERMINATED) { try { /* Obtain lock on current state. */ @@ -522,7 +541,7 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio } /** - * Invokes all [Updatable]s registered with this [SynchronousInteractiveRunManager]. + * Invokes all [Updatable]s registered with this [InteractiveSynchronousRunManager]. */ private fun invokeUpdatables() { this.updatables.forEach { @@ -537,7 +556,7 @@ class SynchronousInteractiveRunManager(val run: InteractiveSynchronousCompetitio } /** - * This is an internal method that facilitates internal state updates to this [SynchronousInteractiveRunManager], + * This is an internal method that facilitates internal state updates to this [InteractiveSynchronousRunManager], * i.e., status updates that are not triggered by an outside interaction. */ private fun internalStateUpdate() { diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt deleted file mode 100644 index 28e217244..000000000 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.dres.run - -import dev.dres.data.model.run.NonInteractiveTask -import dev.dres.data.model.run.SubmissionBatch - -interface NonInteractiveRunManager : RunManager { - - fun addSubmissionBatch(batch: SubmissionBatch<*>) - - override fun tasks(): List - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/SynchronousNonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveSynchronousRunManager.kt similarity index 82% rename from backend/src/main/kotlin/dev/dres/run/SynchronousNonInteractiveRunManager.kt rename to backend/src/main/kotlin/dev/dres/run/NonInteractiveSynchronousRunManager.kt index e50550fa2..a036767a1 100644 --- a/backend/src/main/kotlin/dev/dres/run/SynchronousNonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveSynchronousRunManager.kt @@ -6,10 +6,7 @@ import dev.dres.api.rest.types.run.websocket.ClientMessageType import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.NonInteractiveCompetitionRun -import dev.dres.data.model.run.NonInteractiveTask -import dev.dres.data.model.run.SubmissionBatch -import dev.dres.data.model.run.TaskId +import dev.dres.data.model.run.* import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.ScoreboardsUpdatable import dev.dres.run.validation.interfaces.JudgementValidator @@ -19,28 +16,28 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read -class SynchronousNonInteractiveRunManager(val run: NonInteractiveCompetitionRun) : NonInteractiveRunManager { +class NonInteractiveSynchronousRunManager(val run: NonInteractiveCompetitionRun) : RunManager { private val SCOREBOARD_UPDATE_INTERVAL_MS = 10_000L // TODO make configurable private val LOGGER = LoggerFactory.getLogger(this.javaClass) - /** A lock for state changes to this [SynchronousInteractiveRunManager]. */ + /** A lock for state changes to this [InteractiveSynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() - /** Run ID of this [SynchronousInteractiveRunManager]. */ + /** Run ID of this [InteractiveSynchronousRunManager]. */ override val id: UID get() = this.run.id - /** Name of this [SynchronousInteractiveRunManager]. */ + /** Name of this [InteractiveSynchronousRunManager]. */ override val name: String get() = this.run.name - /** The [CompetitionDescription] executed by this [SynchronousInteractiveRunManager]. */ + /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ override val competitionDescription: CompetitionDescription get() = this.run.competitionDescription - /** The internal [ScoreboardsUpdatable] instance for this [SynchronousInteractiveRunManager]. */ + /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoreboardsUpdatable = ScoreboardsUpdatable(this.competitionDescription.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) override val scoreboards: List @@ -60,15 +57,15 @@ class SynchronousNonInteractiveRunManager(val run: NonInteractiveCompetitionRun) override val judgementValidators: List get() = this.run.tasks.map { it.validator }.filterIsInstance(JudgementValidator::class.java) - override fun start() { + override fun start(context: RunActionContext) { TODO("Not yet implemented") } - override fun end() { + override fun end(context: RunActionContext) { TODO("Not yet implemented") } - override fun taskCount(): Int = this.run.tasks.size + override fun taskCount(context: RunActionContext): Int = this.run.tasks.size private val viewerMap: MutableMap = mutableMapOf() @@ -141,7 +138,7 @@ class SynchronousNonInteractiveRunManager(val run: NonInteractiveCompetitionRun) private val updatedTasks = LinkedBlockingQueue>>>() - override fun addSubmissionBatch(batch: SubmissionBatch<*>) = this.stateLock.read{ + fun addSubmissionBatch(batch: SubmissionBatch<*>) = this.stateLock.read{ check(this.status == RunManagerStatus.RUNNING_TASK) { "SynchronousNonInteractiveRunManager is in status ${this.status} and can currently not accept submissions." } @@ -155,5 +152,5 @@ class SynchronousNonInteractiveRunManager(val run: NonInteractiveCompetitionRun) } - override fun tasks(): List = this.run.tasks + override fun tasks(context: RunActionContext): List = this.run.tasks } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index d6592327c..644128893 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -72,8 +72,8 @@ object RunExecutor : Consumer { this.runs = runs this.runs.filter { !it.hasEnded }.forEach { //TODO needs more distinction val run = when(it) { - is InteractiveSynchronousCompetitionRun -> SynchronousInteractiveRunManager(it) - is NonInteractiveCompetitionRun -> SynchronousNonInteractiveRunManager(it) + is InteractiveSynchronousCompetitionRun -> InteractiveSynchronousRunManager(it) + is NonInteractiveCompetitionRun -> NonInteractiveSynchronousRunManager(it) else -> throw NotImplementedError("No matching run manager found for $it") } this.schedule(run) diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index e37f143fd..1963df147 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.types.run.websocket.ClientMessage import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.Submission import dev.dres.data.model.run.Task import dev.dres.run.score.scoreboard.Scoreboard @@ -46,7 +47,7 @@ interface RunManager : Runnable { * * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.CREATED] */ - fun start() + fun start(context: RunActionContext) /** * Ends this [RunManager] moving [RunManager.status] from [RunManagerStatus.ACTIVE] to @@ -57,7 +58,7 @@ interface RunManager : Runnable { * * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ - fun end() + fun end(context: RunActionContext) /** @@ -65,9 +66,9 @@ interface RunManager : Runnable { * * @return The number of [InteractiveSynchronousCompetitionRun.TaskRun]s held by this [RunManager] */ - fun taskCount(): Int + fun taskCount(context: RunActionContext): Int - fun tasks(): List + fun tasks(context: RunActionContext): List /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 14e7929ba..a8edfd73f 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -1,12 +1,13 @@ package dev.dres.run.updatables import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.SubmissionStatus import dev.dres.run.InteractiveRunManager import dev.dres.run.RunManagerStatus import java.util.concurrent.atomic.AtomicInteger -class EndTaskUpdatable(private val run: InteractiveRunManager) : Updatable { +class EndTaskUpdatable(private val run: InteractiveRunManager, private val context: RunActionContext) : Updatable { override val phase: Phase = Phase.MAIN @@ -16,7 +17,7 @@ class EndTaskUpdatable(private val run: InteractiveRunManager) : Updatable { override fun update(status: RunManagerStatus) { val limitingFilter = run.currentTaskRun?.taskDescription?.taskType?.filter?.find{ it.option == TaskType.SubmissionFilterType.LIMIT_CORRECT_PER_TEAM } ?: return val limit = limitingFilter.parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1 - if (this.run.timeLeft() > 0) { + if (this.run.timeLeft(context) > 0) { val taskRun = this.run.currentTaskRun if (taskRun != null && this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { /* Determine of all teams have submitted . */ @@ -26,7 +27,7 @@ class EndTaskUpdatable(private val run: InteractiveRunManager) : Updatable { /* Do all teams have reached the limit of correct submissions ? */ if (allDone) { - this.run.abortTask() + this.run.abortTask(context) this.submissions.set(0) } } From 6a110e1aa81d550920d9f000a92d5e9b925839c2 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 2 Mar 2021 19:58:49 +0100 Subject: [PATCH 33/95] Fixed order of field initialization --- .../run/InteractiveAsynchronousCompetitionRun.kt | 13 +++++++++++++ .../run/InteractiveSynchronousCompetitionRun.kt | 12 ++++++++++++ .../data/model/run/NonInteractiveCompetitonRun.kt | 4 ++++ .../src/main/kotlin/dev/dres/data/model/run/Task.kt | 9 +++------ .../handlers/SubmissionStatisticsHandler.kt | 2 +- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetitionRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetitionRun.kt index 7ffc43003..fdd105c67 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetitionRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetitionRun.kt @@ -6,6 +6,9 @@ import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.TaskDescriptionId import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun.TaskRun +import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.validation.interfaces.SubmissionValidator class InteractiveAsynchronousCompetitionRun(override var id: CompetitionRunId, name: String, competitionDescription: CompetitionDescription): CompetitionRun(id, name, competitionDescription) { @@ -25,6 +28,8 @@ class InteractiveAsynchronousCompetitionRun(override var id: CompetitionRunId, n /** List of [Submission]s* registered for this [TaskRun]. */ val submissions: List = mutableListOf() + + override fun addSubmission(submission: Submission) { TODO("Not yet implemented") } @@ -33,6 +38,14 @@ class InteractiveAsynchronousCompetitionRun(override var id: CompetitionRunId, n get() = this@InteractiveAsynchronousCompetitionRun.competitionDescription .tasks.find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") + @Transient + override val filter: SubmissionFilter = taskDescription.newFilter() + + @Transient + override val scorer: TaskRunScorer = taskDescription.newScorer() + + @Transient + override val validator: SubmissionValidator = newValidator() } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetitionRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetitionRun.kt index 64691e247..07420335b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetitionRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetitionRun.kt @@ -6,6 +6,9 @@ import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.TaskDescriptionId import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun.TaskRun +import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.validation.interfaces.SubmissionValidator import java.util.* @@ -84,6 +87,15 @@ class InteractiveSynchronousCompetitionRun(override var id: CompetitionRunId, na override val taskDescription: TaskDescription = this@InteractiveSynchronousCompetitionRun.competitionDescription.tasks.find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") + @Transient + override val filter: SubmissionFilter = taskDescription.newFilter() + + @Transient + override val scorer: TaskRunScorer = taskDescription.newScorer() + + @Transient + override val validator: SubmissionValidator = newValidator() + /** Duration of this [TaskRun]. Defaults to the duration specified in the [TaskDescription]. */ @Volatile diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetitonRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetitonRun.kt index f948a1a3b..25d021678 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetitonRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetitonRun.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.TaskDescriptionId import dev.dres.data.model.competition.TeamId +import dev.dres.run.score.interfaces.TaskRunScorer open class NonInteractiveCompetitionRun(override var id: CompetitionRunId, name: String, competitionDescription: CompetitionDescription): CompetitionRun(id, name, competitionDescription) { @@ -28,6 +29,9 @@ open class NonInteractiveCompetitionRun(override var id: CompetitionRunId, name: override val taskDescription: TaskDescription = this@NonInteractiveCompetitionRun.competitionDescription.tasks .find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") + @Transient + override val scorer: TaskRunScorer = taskDescription.newScorer() + } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt index a5dcf7a31..a40ddde0d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -27,8 +27,7 @@ abstract class Task { abstract val taskDescription: TaskDescription /** The [TaskRunScorer] used to update score for this [Task]. */ - @Transient - val scorer: TaskRunScorer = taskDescription.newScorer() + abstract val scorer: TaskRunScorer } @@ -45,12 +44,10 @@ abstract class InteractiveTask : Task() { internal set /** The [SubmissionFilter] used to filter [Submission]s. */ - @Transient - val filter: SubmissionFilter = taskDescription.newFilter() + abstract val filter: SubmissionFilter /** The [SubmissionValidator] used to validate [Submission]s. */ - @Transient - val validator: SubmissionValidator = newValidator() + abstract val validator: SubmissionValidator /** * Generates and returns a new [SubmissionValidator] for this [TaskDescription]. Depending diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index 9884198b3..3b8249499 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -62,4 +62,4 @@ class SubmissionStatisticsHandler : StreamEventHandler { } -} \ No newline at end of file +} From 2a58eb687d03fe6f70c7e7a84bccb39f558affe2 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 3 Mar 2021 12:41:18 +0100 Subject: [PATCH 34/95] Regenerated openapi bindings --- frontend/openapi/.openapi-generator/FILES | 192 ++++++++--------- frontend/openapi/README.md | 4 +- frontend/openapi/api.module.ts | 1 + frontend/openapi/api/api.ts | 4 +- frontend/openapi/api/clientRunInfo.service.ts | 197 ++++++++++++++++++ frontend/openapi/model/clientRunInfo.ts | 32 +++ frontend/openapi/model/clientRunInfoList.ts | 18 ++ frontend/openapi/model/clientTaskInfo.ts | 21 ++ .../model/configuredOptionTargetType.ts | 5 +- frontend/openapi/model/models.ts | 3 + frontend/openapi/model/queryEvent.ts | 17 +- frontend/openapi/model/queryEventLog.ts | 3 - frontend/openapi/model/queryResult.ts | 4 +- frontend/openapi/model/queryResultLog.ts | 9 +- .../model/restTaskDescriptionTarget.ts | 5 +- frontend/openapi/package.json | 2 +- 16 files changed, 402 insertions(+), 115 deletions(-) create mode 100644 frontend/openapi/api/clientRunInfo.service.ts create mode 100644 frontend/openapi/model/clientRunInfo.ts create mode 100644 frontend/openapi/model/clientRunInfoList.ts create mode 100644 frontend/openapi/model/clientTaskInfo.ts diff --git a/frontend/openapi/.openapi-generator/FILES b/frontend/openapi/.openapi-generator/FILES index 21754f19f..a4598cf3e 100644 --- a/frontend/openapi/.openapi-generator/FILES +++ b/frontend/openapi/.openapi-generator/FILES @@ -1,94 +1,98 @@ -.gitignore -README.md -api.module.ts -api/api.ts -api/audit.service.ts -api/collection.service.ts -api/competition.service.ts -api/competitionRun.service.ts -api/competitionRunAdmin.service.ts -api/competitionRunScores.service.ts -api/judgement.service.ts -api/log.service.ts -api/status.service.ts -api/submission.service.ts -api/user.service.ts -configuration.ts -encoder.ts -git_push.sh -index.ts -model/auditLogInfo.ts -model/competitionCreate.ts -model/competitionOverview.ts -model/competitionStartMessage.ts -model/configuredOptionOptions.ts -model/configuredOptionQueryComponentType.ts -model/configuredOptionScoringType.ts -model/configuredOptionSubmissionFilterType.ts -model/configuredOptionTargetType.ts -model/contentElement.ts -model/currentTime.ts -model/errorStatus.ts -model/judgement.ts -model/judgementRequest.ts -model/judgementValidatorStatus.ts -model/judgementVote.ts -model/loginRequest.ts -model/models.ts -model/queryEvent.ts -model/queryEventLog.ts -model/queryResult.ts -model/queryResultLog.ts -model/restAuditLogEntry.ts -model/restCompetitionDescription.ts -model/restCompetitionEndAuditLogEntry.ts -model/restCompetitionEndAuditLogEntryAllOf.ts -model/restCompetitionStartAuditLogEntry.ts -model/restDetailedTeam.ts -model/restFullMediaCollection.ts -model/restJudgementAuditLogEntry.ts -model/restJudgementAuditLogEntryAllOf.ts -model/restLoginAuditLogEntry.ts -model/restLoginAuditLogEntryAllOf.ts -model/restLogoutAuditLogEntry.ts -model/restLogoutAuditLogEntryAllOf.ts -model/restMediaCollection.ts -model/restMediaItem.ts -model/restPrepareJudgementAuditLogEntry.ts -model/restPrepareJudgementAuditLogEntryAllOf.ts -model/restSubmissionAuditLogEntry.ts -model/restSubmissionAuditLogEntryAllOf.ts -model/restTaskDescription.ts -model/restTaskDescriptionComponent.ts -model/restTaskDescriptionTarget.ts -model/restTaskDescriptionTargetItem.ts -model/restTaskEndAuditLogEntry.ts -model/restTaskEndAuditLogEntryAllOf.ts -model/restTaskModifiedAuditLogEntry.ts -model/restTaskModifiedAuditLogEntryAllOf.ts -model/restTaskStartAuditLogEntry.ts -model/restTeam.ts -model/runInfo.ts -model/runState.ts -model/score.ts -model/scoreOverview.ts -model/scoreSeries.ts -model/scoreSeriesPoint.ts -model/sessionId.ts -model/submissionInfo.ts -model/successStatus.ts -model/taskGroup.ts -model/taskHint.ts -model/taskInfo.ts -model/taskTarget.ts -model/taskType.ts -model/teamInfo.ts -model/temporalPoint.ts -model/temporalRange.ts -model/userDetails.ts -model/userRequest.ts -model/viewerInfo.ts -ng-package.json -package.json -tsconfig.json -variables.ts +.gitignore +README.md +api.module.ts +api/api.ts +api/audit.service.ts +api/clientRunInfo.service.ts +api/collection.service.ts +api/competition.service.ts +api/competitionRun.service.ts +api/competitionRunAdmin.service.ts +api/competitionRunScores.service.ts +api/judgement.service.ts +api/log.service.ts +api/status.service.ts +api/submission.service.ts +api/user.service.ts +configuration.ts +encoder.ts +git_push.sh +index.ts +model/auditLogInfo.ts +model/clientRunInfo.ts +model/clientRunInfoList.ts +model/clientTaskInfo.ts +model/competitionCreate.ts +model/competitionOverview.ts +model/competitionStartMessage.ts +model/configuredOptionOptions.ts +model/configuredOptionQueryComponentType.ts +model/configuredOptionScoringType.ts +model/configuredOptionSubmissionFilterType.ts +model/configuredOptionTargetType.ts +model/contentElement.ts +model/currentTime.ts +model/errorStatus.ts +model/judgement.ts +model/judgementRequest.ts +model/judgementValidatorStatus.ts +model/judgementVote.ts +model/loginRequest.ts +model/models.ts +model/queryEvent.ts +model/queryEventLog.ts +model/queryResult.ts +model/queryResultLog.ts +model/restAuditLogEntry.ts +model/restCompetitionDescription.ts +model/restCompetitionEndAuditLogEntry.ts +model/restCompetitionEndAuditLogEntryAllOf.ts +model/restCompetitionStartAuditLogEntry.ts +model/restDetailedTeam.ts +model/restFullMediaCollection.ts +model/restJudgementAuditLogEntry.ts +model/restJudgementAuditLogEntryAllOf.ts +model/restLoginAuditLogEntry.ts +model/restLoginAuditLogEntryAllOf.ts +model/restLogoutAuditLogEntry.ts +model/restLogoutAuditLogEntryAllOf.ts +model/restMediaCollection.ts +model/restMediaItem.ts +model/restPrepareJudgementAuditLogEntry.ts +model/restPrepareJudgementAuditLogEntryAllOf.ts +model/restSubmissionAuditLogEntry.ts +model/restSubmissionAuditLogEntryAllOf.ts +model/restTaskDescription.ts +model/restTaskDescriptionComponent.ts +model/restTaskDescriptionTarget.ts +model/restTaskDescriptionTargetItem.ts +model/restTaskEndAuditLogEntry.ts +model/restTaskEndAuditLogEntryAllOf.ts +model/restTaskModifiedAuditLogEntry.ts +model/restTaskModifiedAuditLogEntryAllOf.ts +model/restTaskStartAuditLogEntry.ts +model/restTeam.ts +model/runInfo.ts +model/runState.ts +model/score.ts +model/scoreOverview.ts +model/scoreSeries.ts +model/scoreSeriesPoint.ts +model/sessionId.ts +model/submissionInfo.ts +model/successStatus.ts +model/taskGroup.ts +model/taskHint.ts +model/taskInfo.ts +model/taskTarget.ts +model/taskType.ts +model/teamInfo.ts +model/temporalPoint.ts +model/temporalRange.ts +model/userDetails.ts +model/userRequest.ts +model/viewerInfo.ts +ng-package.json +package.json +tsconfig.json +variables.ts diff --git a/frontend/openapi/README.md b/frontend/openapi/README.md index adb6e3a85..fa956f61e 100644 --- a/frontend/openapi/README.md +++ b/frontend/openapi/README.md @@ -1,4 +1,4 @@ -## @dres-openapi/api@1.0-SNAPSHOT.202102011837 +## @dres-openapi/api@1.0-SNAPSHOT.202103031232 ### Building @@ -19,7 +19,7 @@ Navigate to the folder of your consuming project and run one of next commands. _published:_ ``` -npm install @dres-openapi/api@1.0-SNAPSHOT.202102011837 --save +npm install @dres-openapi/api@1.0-SNAPSHOT.202103031232 --save ``` _without publishing (not recommended):_ diff --git a/frontend/openapi/api.module.ts b/frontend/openapi/api.module.ts index f3a9f000b..f654ad3bc 100644 --- a/frontend/openapi/api.module.ts +++ b/frontend/openapi/api.module.ts @@ -3,6 +3,7 @@ import { Configuration } from './configuration'; import { HttpClient } from '@angular/common/http'; import { AuditService } from './api/audit.service'; +import { ClientRunInfoService } from './api/clientRunInfo.service'; import { CollectionService } from './api/collection.service'; import { CompetitionService } from './api/competition.service'; import { CompetitionRunService } from './api/competitionRun.service'; diff --git a/frontend/openapi/api/api.ts b/frontend/openapi/api/api.ts index 8342f1c45..3b2a068db 100644 --- a/frontend/openapi/api/api.ts +++ b/frontend/openapi/api/api.ts @@ -1,5 +1,7 @@ export * from './audit.service'; import { AuditService } from './audit.service'; +export * from './clientRunInfo.service'; +import { ClientRunInfoService } from './clientRunInfo.service'; export * from './collection.service'; import { CollectionService } from './collection.service'; export * from './competition.service'; @@ -20,4 +22,4 @@ export * from './submission.service'; import { SubmissionService } from './submission.service'; export * from './user.service'; import { UserService } from './user.service'; -export const APIS = [AuditService, CollectionService, CompetitionService, CompetitionRunService, CompetitionRunAdminService, CompetitionRunScoresService, JudgementService, LogService, StatusService, SubmissionService, UserService]; +export const APIS = [AuditService, ClientRunInfoService, CollectionService, CompetitionService, CompetitionRunService, CompetitionRunAdminService, CompetitionRunScoresService, JudgementService, LogService, StatusService, SubmissionService, UserService]; diff --git a/frontend/openapi/api/clientRunInfo.service.ts b/frontend/openapi/api/clientRunInfo.service.ts new file mode 100644 index 000000000..e03da7070 --- /dev/null +++ b/frontend/openapi/api/clientRunInfo.service.ts @@ -0,0 +1,197 @@ +/** + * DRES API + * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +import { ClientRunInfoList } from '../model/models'; +import { ClientTaskInfo } from '../model/models'; +import { ErrorStatus } from '../model/models'; + +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; + + + +@Injectable({ + providedIn: 'root' +}) +export class ClientRunInfoService { + + protected basePath = 'http://localhost'; + public defaultHeaders = new HttpHeaders(); + public configuration = new Configuration(); + public encoder: HttpParameterCodec; + + constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { + if (configuration) { + this.configuration = configuration; + } + if (typeof this.configuration.basePath !== 'string') { + if (typeof basePath !== 'string') { + basePath = this.basePath; + } + this.configuration.basePath = basePath; + } + this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); + } + + + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + + /** + * Returns an overview of the currently active task for a run + * @param runId + * @param session Session Token + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getApiRuninfoCurrenttaskWithRunid(runId: string, session: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public getApiRuninfoCurrenttaskWithRunid(runId: string, session: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getApiRuninfoCurrenttaskWithRunid(runId: string, session: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getApiRuninfoCurrenttaskWithRunid(runId: string, session: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (runId === null || runId === undefined) { + throw new Error('Required parameter runId was null or undefined when calling getApiRuninfoCurrenttaskWithRunid.'); + } + if (session === null || session === undefined) { + throw new Error('Required parameter session was null or undefined when calling getApiRuninfoCurrenttaskWithRunid.'); + } + + let queryParameters = new HttpParams({encoder: this.encoder}); + if (session !== undefined && session !== null) { + queryParameters = this.addToHttpParams(queryParameters, + session, 'session'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get(`${this.configuration.basePath}/api/runInfo/currentTask/${encodeURIComponent(String(runId))}`, + { + params: queryParameters, + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Lists an overview of all competition runs visible to the current client + * @param session Session Token + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getApiRuninfoList(session: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public getApiRuninfoList(session: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getApiRuninfoList(session: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getApiRuninfoList(session: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (session === null || session === undefined) { + throw new Error('Required parameter session was null or undefined when calling getApiRuninfoList.'); + } + + let queryParameters = new HttpParams({encoder: this.encoder}); + if (session !== undefined && session !== null) { + queryParameters = this.addToHttpParams(queryParameters, + session, 'session'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get(`${this.configuration.basePath}/api/runInfo/list`, + { + params: queryParameters, + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + +} diff --git a/frontend/openapi/model/clientRunInfo.ts b/frontend/openapi/model/clientRunInfo.ts new file mode 100644 index 000000000..78b8f7243 --- /dev/null +++ b/frontend/openapi/model/clientRunInfo.ts @@ -0,0 +1,32 @@ +/** + * DRES API + * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ClientRunInfo { + id: string; + name: string; + description?: string; + status: ClientRunInfo.StatusEnum; +} +export namespace ClientRunInfo { + export type StatusEnum = 'CREATED' | 'ACTIVE' | 'PREPARING_TASK' | 'RUNNING_TASK' | 'TASK_ENDED' | 'TERMINATED'; + export const StatusEnum = { + CREATED: 'CREATED' as StatusEnum, + ACTIVE: 'ACTIVE' as StatusEnum, + PREPARING_TASK: 'PREPARING_TASK' as StatusEnum, + RUNNING_TASK: 'RUNNING_TASK' as StatusEnum, + TASK_ENDED: 'TASK_ENDED' as StatusEnum, + TERMINATED: 'TERMINATED' as StatusEnum + }; +} + + diff --git a/frontend/openapi/model/clientRunInfoList.ts b/frontend/openapi/model/clientRunInfoList.ts new file mode 100644 index 000000000..8f84cbdc5 --- /dev/null +++ b/frontend/openapi/model/clientRunInfoList.ts @@ -0,0 +1,18 @@ +/** + * DRES API + * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { ClientRunInfo } from './clientRunInfo'; + + +export interface ClientRunInfoList { + runs: Array; +} + diff --git a/frontend/openapi/model/clientTaskInfo.ts b/frontend/openapi/model/clientTaskInfo.ts new file mode 100644 index 000000000..55ed7b69b --- /dev/null +++ b/frontend/openapi/model/clientTaskInfo.ts @@ -0,0 +1,21 @@ +/** + * DRES API + * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ClientTaskInfo { + id: string; + name: string; + taskGroup: string; + remainingTime: number; + running: boolean; +} + diff --git a/frontend/openapi/model/configuredOptionTargetType.ts b/frontend/openapi/model/configuredOptionTargetType.ts index 59ecb003d..acbf9925d 100644 --- a/frontend/openapi/model/configuredOptionTargetType.ts +++ b/frontend/openapi/model/configuredOptionTargetType.ts @@ -16,12 +16,13 @@ export interface ConfiguredOptionTargetType { parameters: { [key: string]: string; }; } export namespace ConfiguredOptionTargetType { - export type OptionEnum = 'SINGLE_MEDIA_ITEM' | 'SINGLE_MEDIA_SEGMENT' | 'MULTIPLE_MEDIA_ITEMS' | 'JUDGEMENT'; + export type OptionEnum = 'SINGLE_MEDIA_ITEM' | 'SINGLE_MEDIA_SEGMENT' | 'MULTIPLE_MEDIA_ITEMS' | 'JUDGEMENT' | 'VOTE'; export const OptionEnum = { SINGLE_MEDIA_ITEM: 'SINGLE_MEDIA_ITEM' as OptionEnum, SINGLE_MEDIA_SEGMENT: 'SINGLE_MEDIA_SEGMENT' as OptionEnum, MULTIPLE_MEDIA_ITEMS: 'MULTIPLE_MEDIA_ITEMS' as OptionEnum, - JUDGEMENT: 'JUDGEMENT' as OptionEnum + JUDGEMENT: 'JUDGEMENT' as OptionEnum, + VOTE: 'VOTE' as OptionEnum }; } diff --git a/frontend/openapi/model/models.ts b/frontend/openapi/model/models.ts index 5dd541895..ef6eea13f 100644 --- a/frontend/openapi/model/models.ts +++ b/frontend/openapi/model/models.ts @@ -1,4 +1,7 @@ export * from './auditLogInfo'; +export * from './clientRunInfo'; +export * from './clientRunInfoList'; +export * from './clientTaskInfo'; export * from './competitionCreate'; export * from './competitionOverview'; export * from './competitionStartMessage'; diff --git a/frontend/openapi/model/queryEvent.ts b/frontend/openapi/model/queryEvent.ts index d0f851a66..5f56b2a7e 100644 --- a/frontend/openapi/model/queryEvent.ts +++ b/frontend/openapi/model/queryEvent.ts @@ -13,8 +13,21 @@ export interface QueryEvent { timestamp: number; - category: string; - type: Array; + category: QueryEvent.CategoryEnum; + type: string; value: string; } +export namespace QueryEvent { + export type CategoryEnum = 'TEXT' | 'IMAGE' | 'SKETCH' | 'FILTER' | 'BROWSING' | 'COOPERATION' | 'OTHER'; + export const CategoryEnum = { + TEXT: 'TEXT' as CategoryEnum, + IMAGE: 'IMAGE' as CategoryEnum, + SKETCH: 'SKETCH' as CategoryEnum, + FILTER: 'FILTER' as CategoryEnum, + BROWSING: 'BROWSING' as CategoryEnum, + COOPERATION: 'COOPERATION' as CategoryEnum, + OTHER: 'OTHER' as CategoryEnum + }; +} + diff --git a/frontend/openapi/model/queryEventLog.ts b/frontend/openapi/model/queryEventLog.ts index dbc7706a4..4757cd514 100644 --- a/frontend/openapi/model/queryEventLog.ts +++ b/frontend/openapi/model/queryEventLog.ts @@ -15,8 +15,5 @@ import { QueryEvent } from './queryEvent'; export interface QueryEventLog { timestamp: number; events: Array; - type: string; - serverTimeStamp?: number; - serverTimeStamp$dres: number; } diff --git a/frontend/openapi/model/queryResult.ts b/frontend/openapi/model/queryResult.ts index 5fb6c640c..5fe8d749d 100644 --- a/frontend/openapi/model/queryResult.ts +++ b/frontend/openapi/model/queryResult.ts @@ -12,8 +12,8 @@ export interface QueryResult { - video: string; - shot?: number; + item: string; + segment?: number; frame?: number; score?: number; rank?: number; diff --git a/frontend/openapi/model/queryResultLog.ts b/frontend/openapi/model/queryResultLog.ts index 2fbd32a3d..66269f991 100644 --- a/frontend/openapi/model/queryResultLog.ts +++ b/frontend/openapi/model/queryResultLog.ts @@ -10,17 +10,14 @@ * Do not edit the class manually. */ import { QueryResult } from './queryResult'; +import { QueryEvent } from './queryEvent'; export interface QueryResultLog { timestamp: number; - values: Array; - usedCategories: Array; - usedTypes: Array; - sortType: Array; + sortType: string; resultSetAvailability: string; results: Array; - serverTimeStamp?: number; - serverTimeStamp$dres: number; + events: Array; } diff --git a/frontend/openapi/model/restTaskDescriptionTarget.ts b/frontend/openapi/model/restTaskDescriptionTarget.ts index d014943e9..d7513d9c1 100644 --- a/frontend/openapi/model/restTaskDescriptionTarget.ts +++ b/frontend/openapi/model/restTaskDescriptionTarget.ts @@ -17,12 +17,13 @@ export interface RestTaskDescriptionTarget { mediaItems: Array; } export namespace RestTaskDescriptionTarget { - export type TypeEnum = 'SINGLE_MEDIA_ITEM' | 'SINGLE_MEDIA_SEGMENT' | 'MULTIPLE_MEDIA_ITEMS' | 'JUDGEMENT'; + export type TypeEnum = 'SINGLE_MEDIA_ITEM' | 'SINGLE_MEDIA_SEGMENT' | 'MULTIPLE_MEDIA_ITEMS' | 'JUDGEMENT' | 'VOTE'; export const TypeEnum = { SINGLE_MEDIA_ITEM: 'SINGLE_MEDIA_ITEM' as TypeEnum, SINGLE_MEDIA_SEGMENT: 'SINGLE_MEDIA_SEGMENT' as TypeEnum, MULTIPLE_MEDIA_ITEMS: 'MULTIPLE_MEDIA_ITEMS' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum + JUDGEMENT: 'JUDGEMENT' as TypeEnum, + VOTE: 'VOTE' as TypeEnum }; } diff --git a/frontend/openapi/package.json b/frontend/openapi/package.json index 0b3eef11e..4d216cdff 100644 --- a/frontend/openapi/package.json +++ b/frontend/openapi/package.json @@ -1,6 +1,6 @@ { "name": "@dres-openapi/api", - "version": "1.0-SNAPSHOT.202102011837", + "version": "1.0-SNAPSHOT.202103031232", "description": "OpenAPI client for @dres-openapi/api", "author": "OpenAPI-Generator Contributors", "keywords": [ From 83c91f5ee2e9bd2f807d0ef98ebb34c9b0ae41f3 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 3 Mar 2021 12:57:43 +0100 Subject: [PATCH 35/95] Backend funktionality for #163 mostly in place, frontend functionality partially working --- .../api/rest/handler/SubmissionHandler.kt | 10 +++++----- .../validation/judged/BasicVoteValidator.kt | 2 +- .../competition-form.builder.ts | 2 ++ .../judgement-voting-viewer.component.ts | 19 ++++++++++++------- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index 97128ba70..033a86bef 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -114,12 +114,12 @@ class SubmissionHandler (val collections: DAO, private val item @OpenApi(summary = "Endpoint to accept submissions", path = "/submit", queryParams = [ - OpenApiParam(PARAMETER_NAME_COLLECTION, String::class, "Collection identifier. Optional, in which case the default collection for the run will be considered."), + OpenApiParam(PARAMETER_NAME_COLLECTION, String::class, "Collection identifier. Optional, in which case the default collection for the run will be considered.", allowEmptyValue = true), OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), - OpenApiParam(PARAMETER_NAME_FRAME, Int::class, "Frame number for media with temporal progression (e.g. video)."), - OpenApiParam(PARAMETER_NAME_SHOT, Int::class, "Shot number for media with temporal progression (e.g. video)."), - OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g. video)."), - OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + OpenApiParam(PARAMETER_NAME_FRAME, Int::class, "Frame number for media with temporal progression (e.g. video).", allowEmptyValue = true), + OpenApiParam(PARAMETER_NAME_SHOT, Int::class, "Shot number for media with temporal progression (e.g. video).", allowEmptyValue = true), + OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g. video).", allowEmptyValue = true), + OpenApiParam("session", String::class, "Session Token") ], tags = ["Submission"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index 12296f782..0e3c86a5f 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -60,7 +60,7 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() val sum = voteCountMap.values.sum() if (sum < minimumVotes) return false val max = voteCountMap.values.maxOrNull() ?: 0 - return (sum - max) >= voteDifference + return (sum - max) <= voteDifference } override fun nextSubmissionToVoteOn(): Submission? = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 3f0190aac..a1fcbdcd2 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -208,6 +208,8 @@ export class CompetitionFormBuilder { return new FormArray([this.singleMediaSegmentTargetForm(this.data?.target?.mediaItems[0])]); case 'JUDGEMENT': return new FormArray([]); + case 'VOTE': + return new FormArray([]); } } diff --git a/frontend/src/app/judgement/judgement-voting-viewer.component.ts b/frontend/src/app/judgement/judgement-voting-viewer.component.ts index 1c9a29333..8ba1244ac 100644 --- a/frontend/src/app/judgement/judgement-voting-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-voting-viewer.component.ts @@ -20,7 +20,7 @@ export class JudgementVotingViewerComponent implements OnInit { private requestSub: Subscription; isJudgmentAvailable = false; - judgementRequest: JudgementRequest; + judgementRequest: JudgementRequest = null; observableJudgementRequest: BehaviorSubject = new BehaviorSubject(null); voteClientPath: Observable; @@ -42,21 +42,26 @@ export class JudgementVotingViewerComponent implements OnInit { this.requestSub = interval(this.pollingFrequency).pipe( withLatestFrom(this.runId), switchMap(([i, runId]) => { - if (this.runId && !this.isJudgmentAvailable) { + if (this.runId) { return this.judgementService.getApiRunWithRunidVoteNext(runId, 'response').pipe( map((req: HttpResponse) => { if (req.status === 202) { this.isJudgmentAvailable = false; + this.judgementRequest = null; console.log('currently nothing for audience to vote on'); return null; } else { - return req.body; + const lastRequest = req.body; + if (this.judgementRequest !== null && lastRequest.token === this.judgementRequest.token) { + return of(null); // still the same, no action required + } + return lastRequest; } }), catchError(err => { - const httperr = err as HttpErrorResponse; - if (httperr) { - if (httperr.status === 404) { + const httpErr = err as HttpErrorResponse; + if (httpErr) { + if (httpErr.status === 404) { const snack = this.snackBar.open(`Invalid runId: ${runId}`, null, {duration: 2000}); snack.afterDismissed().subscribe(() => { this.router.navigate(['/run/list']); @@ -78,7 +83,7 @@ export class JudgementVotingViewerComponent implements OnInit { console.log(req); this.judgementRequest = req; this.observableJudgementRequest.next(req); - this.isJudgmentAvailable = true; //TODO figure out mechanism to reset this + this.isJudgmentAvailable = true; }); } From 51bd18f3bb8d339338b895f4088ffbb81cf3c0d3 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 3 Mar 2021 13:39:31 +0100 Subject: [PATCH 36/95] Removed frontend openapi bindings --- .gitignore | 3 + backend/build.gradle | 21 +- doc/oas-client.json | 1 + doc/oas.json | 1 + frontend/openapi/.gitignore | 4 - frontend/openapi/.openapi-generator-ignore | 23 - frontend/openapi/.openapi-generator/FILES | 98 --- frontend/openapi/.openapi-generator/VERSION | 1 - frontend/openapi/README.md | 203 ----- frontend/openapi/api.module.ts | 42 - frontend/openapi/api/api.ts | 25 - frontend/openapi/api/audit.service.ts | 228 ------ frontend/openapi/api/clientRunInfo.service.ts | 197 ----- frontend/openapi/api/collection.service.ts | 659 ---------------- frontend/openapi/api/competition.service.ts | 464 ----------- .../openapi/api/competitionRun.service.ts | 543 ------------- .../api/competitionRunAdmin.service.ts | 719 ------------------ .../api/competitionRunScores.service.ts | 322 -------- frontend/openapi/api/default.service.ts | 131 ---- frontend/openapi/api/judgement.service.ts | 339 --------- frontend/openapi/api/log.service.ts | 216 ------ frontend/openapi/api/status.service.ts | 128 ---- frontend/openapi/api/submission.service.ts | 165 ---- frontend/openapi/api/user.service.ts | 569 -------------- frontend/openapi/configuration.ts | 121 --- frontend/openapi/encoder.ts | 20 - frontend/openapi/git_push.sh | 58 -- frontend/openapi/index.ts | 5 - frontend/openapi/model/auditLogEntry.ts | 36 - frontend/openapi/model/auditLogInfo.ts | 19 - frontend/openapi/model/clientRunInfo.ts | 32 - frontend/openapi/model/clientRunInfoList.ts | 18 - frontend/openapi/model/clientTaskInfo.ts | 21 - frontend/openapi/model/competitionCreate.ts | 19 - .../model/competitionEndAuditLogEntry.ts | 30 - .../model/competitionEndAuditLogEntryAllOf.ts | 28 - frontend/openapi/model/competitionOverview.ts | 21 - .../model/competitionStartAuditLogEntry.ts | 47 -- .../openapi/model/competitionStartMessage.ts | 28 - .../openapi/model/configuredOptionOptions.ts | 26 - .../configuredOptionQueryComponentType.ts | 29 - .../model/configuredOptionScoringType.ts | 26 - .../configuredOptionSubmissionFilterType.ts | 30 - .../model/configuredOptionTargetType.ts | 29 - frontend/openapi/model/contentElement.ts | 29 - frontend/openapi/model/currentTime.ts | 17 - frontend/openapi/model/errorStatus.ts | 18 - .../model/externalImageTaskDescriptionHint.ts | 23 - .../externalImageTaskDescriptionHintAllOf.ts | 18 - ...geTaskDescriptionHintAllOfImageLocation.ts | 20 - ...riptionHintAllOfImageLocationFileSystem.ts | 22 - .../model/externalVideoTaskDescriptionHint.ts | 23 - .../externalVideoTaskDescriptionHintAllOf.ts | 18 - frontend/openapi/model/imageItem.ts | 21 - .../model/imageItemTaskDescriptionHint.ts | 23 - .../imageItemTaskDescriptionHintAllOf.ts | 18 - frontend/openapi/model/itemSubmission.ts | 22 - frontend/openapi/model/judgement.ts | 29 - .../openapi/model/judgementAuditLogEntry.ts | 40 - .../model/judgementAuditLogEntryAllOf.ts | 38 - frontend/openapi/model/judgementRequest.ts | 23 - .../model/judgementTaskDescriptionTarget.ts | 17 - .../openapi/model/judgementValidatorStatus.ts | 19 - frontend/openapi/model/judgementVote.ts | 27 - frontend/openapi/model/loginAuditLogEntry.ts | 30 - .../openapi/model/loginAuditLogEntryAllOf.ts | 28 - frontend/openapi/model/loginRequest.ts | 18 - frontend/openapi/model/logoutAuditLogEntry.ts | 29 - .../openapi/model/logoutAuditLogEntryAllOf.ts | 27 - frontend/openapi/model/mediaItem.ts | 21 - frontend/openapi/model/mediaItemTarget.ts | 20 - .../openapi/model/mediaItemTargetAllOf.ts | 18 - frontend/openapi/model/models.ts | 73 -- .../openapi/model/multipleMediaItemTarget.ts | 20 - .../model/multipleMediaItemTargetAllOf.ts | 18 - frontend/openapi/model/queryEvent.ts | 33 - frontend/openapi/model/queryEventLog.ts | 19 - frontend/openapi/model/queryResult.ts | 21 - frontend/openapi/model/queryResultLog.ts | 23 - frontend/openapi/model/restAuditLogEntry.ts | 35 - .../model/restCompetitionDescription.ts | 28 - .../model/restCompetitionEndAuditLogEntry.ts | 46 -- .../restCompetitionEndAuditLogEntryAllOf.ts | 28 - .../restCompetitionStartAuditLogEntry.ts | 46 -- frontend/openapi/model/restDetailedTeam.ts | 21 - .../openapi/model/restFullMediaCollection.ts | 20 - .../model/restJudgementAuditLogEntry.ts | 56 -- .../model/restJudgementAuditLogEntryAllOf.ts | 38 - .../openapi/model/restLoginAuditLogEntry.ts | 46 -- .../model/restLoginAuditLogEntryAllOf.ts | 28 - .../openapi/model/restLogoutAuditLogEntry.ts | 45 -- .../model/restLogoutAuditLogEntryAllOf.ts | 27 - frontend/openapi/model/restMediaCollection.ts | 20 - frontend/openapi/model/restMediaItem.ts | 31 - .../restPrepareJudgementAuditLogEntry.ts | 41 - .../restPrepareJudgementAuditLogEntryAllOf.ts | 20 - .../model/restSubmissionAuditLogEntry.ts | 50 -- .../model/restSubmissionAuditLogEntryAllOf.ts | 32 - frontend/openapi/model/restTaskDescription.ts | 26 - .../model/restTaskDescriptionComponent.ts | 36 - .../model/restTaskDescriptionTarget.ts | 30 - .../model/restTaskDescriptionTargetItem.ts | 19 - .../openapi/model/restTaskEndAuditLogEntry.ts | 47 -- .../model/restTaskEndAuditLogEntryAllOf.ts | 29 - .../model/restTaskModifiedAuditLogEntry.ts | 48 -- .../restTaskModifiedAuditLogEntryAllOf.ts | 30 - .../model/restTaskStartAuditLogEntry.ts | 47 -- frontend/openapi/model/restTeam.ts | 22 - frontend/openapi/model/runInfo.ts | 25 - frontend/openapi/model/runScoreSeries.ts | 20 - frontend/openapi/model/runState.ts | 33 - frontend/openapi/model/score.ts | 18 - frontend/openapi/model/scoreOverview.ts | 20 - frontend/openapi/model/scoreSeries.ts | 20 - frontend/openapi/model/scoreSeriesPoint.ts | 18 - frontend/openapi/model/sessionId.ts | 17 - frontend/openapi/model/submission.ts | 38 - .../openapi/model/submissionAuditLogEntry.ts | 34 - .../model/submissionAuditLogEntryAllOf.ts | 32 - frontend/openapi/model/submissionInfo.ts | 35 - frontend/openapi/model/successStatus.ts | 18 - frontend/openapi/model/taskDescription.ts | 28 - frontend/openapi/model/taskDescriptionHint.ts | 36 - .../openapi/model/taskDescriptionTarget.ts | 17 - .../openapi/model/taskEndAuditLogEntry.ts | 31 - .../model/taskEndAuditLogEntryAllOf.ts | 29 - frontend/openapi/model/taskGroup.ts | 18 - frontend/openapi/model/taskHint.ts | 20 - frontend/openapi/model/taskInfo.ts | 21 - .../model/taskModifiedAuditLogEntry.ts | 32 - .../model/taskModifiedAuditLogEntryAllOf.ts | 30 - frontend/openapi/model/taskRun.ts | 32 - .../openapi/model/taskStartAuditLogEntry.ts | 48 -- frontend/openapi/model/taskTarget.ts | 19 - frontend/openapi/model/taskType.ts | 28 - frontend/openapi/model/team.ts | 22 - frontend/openapi/model/teamInfo.ts | 20 - frontend/openapi/model/temporalPoint.ts | 27 - frontend/openapi/model/temporalRange.ts | 19 - frontend/openapi/model/temporalSubmission.ts | 27 - .../openapi/model/temporalSubmissionAllOf.ts | 20 - .../openapi/model/textTaskDescriptionHint.ts | 22 - .../model/textTaskDescriptionHintAllOf.ts | 17 - frontend/openapi/model/uID.ts | 17 - frontend/openapi/model/userDetails.ts | 30 - frontend/openapi/model/userRequest.ts | 29 - frontend/openapi/model/videoItem.ts | 20 - frontend/openapi/model/videoItemAllOf.ts | 18 - .../videoItemSegmentTaskDescriptionHint.ts | 25 - ...ideoItemSegmentTaskDescriptionHintAllOf.ts | 20 - frontend/openapi/model/videoSegmentTarget.ts | 23 - frontend/openapi/model/viewerInfo.ts | 20 - frontend/openapi/ng-package.json | 6 - frontend/openapi/package.json | 30 - frontend/openapi/tsconfig.json | 28 - frontend/openapi/variables.ts | 9 - 156 files changed, 22 insertions(+), 8744 deletions(-) create mode 100644 doc/oas-client.json create mode 100644 doc/oas.json delete mode 100644 frontend/openapi/.gitignore delete mode 100644 frontend/openapi/.openapi-generator-ignore delete mode 100644 frontend/openapi/.openapi-generator/FILES delete mode 100644 frontend/openapi/.openapi-generator/VERSION delete mode 100644 frontend/openapi/README.md delete mode 100644 frontend/openapi/api.module.ts delete mode 100644 frontend/openapi/api/api.ts delete mode 100644 frontend/openapi/api/audit.service.ts delete mode 100644 frontend/openapi/api/clientRunInfo.service.ts delete mode 100644 frontend/openapi/api/collection.service.ts delete mode 100644 frontend/openapi/api/competition.service.ts delete mode 100644 frontend/openapi/api/competitionRun.service.ts delete mode 100644 frontend/openapi/api/competitionRunAdmin.service.ts delete mode 100644 frontend/openapi/api/competitionRunScores.service.ts delete mode 100644 frontend/openapi/api/default.service.ts delete mode 100644 frontend/openapi/api/judgement.service.ts delete mode 100644 frontend/openapi/api/log.service.ts delete mode 100644 frontend/openapi/api/status.service.ts delete mode 100644 frontend/openapi/api/submission.service.ts delete mode 100644 frontend/openapi/api/user.service.ts delete mode 100644 frontend/openapi/configuration.ts delete mode 100644 frontend/openapi/encoder.ts delete mode 100644 frontend/openapi/git_push.sh delete mode 100644 frontend/openapi/index.ts delete mode 100644 frontend/openapi/model/auditLogEntry.ts delete mode 100644 frontend/openapi/model/auditLogInfo.ts delete mode 100644 frontend/openapi/model/clientRunInfo.ts delete mode 100644 frontend/openapi/model/clientRunInfoList.ts delete mode 100644 frontend/openapi/model/clientTaskInfo.ts delete mode 100644 frontend/openapi/model/competitionCreate.ts delete mode 100644 frontend/openapi/model/competitionEndAuditLogEntry.ts delete mode 100644 frontend/openapi/model/competitionEndAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/competitionOverview.ts delete mode 100644 frontend/openapi/model/competitionStartAuditLogEntry.ts delete mode 100644 frontend/openapi/model/competitionStartMessage.ts delete mode 100644 frontend/openapi/model/configuredOptionOptions.ts delete mode 100644 frontend/openapi/model/configuredOptionQueryComponentType.ts delete mode 100644 frontend/openapi/model/configuredOptionScoringType.ts delete mode 100644 frontend/openapi/model/configuredOptionSubmissionFilterType.ts delete mode 100644 frontend/openapi/model/configuredOptionTargetType.ts delete mode 100644 frontend/openapi/model/contentElement.ts delete mode 100644 frontend/openapi/model/currentTime.ts delete mode 100644 frontend/openapi/model/errorStatus.ts delete mode 100644 frontend/openapi/model/externalImageTaskDescriptionHint.ts delete mode 100644 frontend/openapi/model/externalImageTaskDescriptionHintAllOf.ts delete mode 100644 frontend/openapi/model/externalImageTaskDescriptionHintAllOfImageLocation.ts delete mode 100644 frontend/openapi/model/externalImageTaskDescriptionHintAllOfImageLocationFileSystem.ts delete mode 100644 frontend/openapi/model/externalVideoTaskDescriptionHint.ts delete mode 100644 frontend/openapi/model/externalVideoTaskDescriptionHintAllOf.ts delete mode 100644 frontend/openapi/model/imageItem.ts delete mode 100644 frontend/openapi/model/imageItemTaskDescriptionHint.ts delete mode 100644 frontend/openapi/model/imageItemTaskDescriptionHintAllOf.ts delete mode 100644 frontend/openapi/model/itemSubmission.ts delete mode 100644 frontend/openapi/model/judgement.ts delete mode 100644 frontend/openapi/model/judgementAuditLogEntry.ts delete mode 100644 frontend/openapi/model/judgementAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/judgementRequest.ts delete mode 100644 frontend/openapi/model/judgementTaskDescriptionTarget.ts delete mode 100644 frontend/openapi/model/judgementValidatorStatus.ts delete mode 100644 frontend/openapi/model/judgementVote.ts delete mode 100644 frontend/openapi/model/loginAuditLogEntry.ts delete mode 100644 frontend/openapi/model/loginAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/loginRequest.ts delete mode 100644 frontend/openapi/model/logoutAuditLogEntry.ts delete mode 100644 frontend/openapi/model/logoutAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/mediaItem.ts delete mode 100644 frontend/openapi/model/mediaItemTarget.ts delete mode 100644 frontend/openapi/model/mediaItemTargetAllOf.ts delete mode 100644 frontend/openapi/model/models.ts delete mode 100644 frontend/openapi/model/multipleMediaItemTarget.ts delete mode 100644 frontend/openapi/model/multipleMediaItemTargetAllOf.ts delete mode 100644 frontend/openapi/model/queryEvent.ts delete mode 100644 frontend/openapi/model/queryEventLog.ts delete mode 100644 frontend/openapi/model/queryResult.ts delete mode 100644 frontend/openapi/model/queryResultLog.ts delete mode 100644 frontend/openapi/model/restAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restCompetitionDescription.ts delete mode 100644 frontend/openapi/model/restCompetitionEndAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restCompetitionEndAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/restCompetitionStartAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restDetailedTeam.ts delete mode 100644 frontend/openapi/model/restFullMediaCollection.ts delete mode 100644 frontend/openapi/model/restJudgementAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restJudgementAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/restLoginAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restLoginAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/restLogoutAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restLogoutAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/restMediaCollection.ts delete mode 100644 frontend/openapi/model/restMediaItem.ts delete mode 100644 frontend/openapi/model/restPrepareJudgementAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restPrepareJudgementAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/restSubmissionAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restSubmissionAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/restTaskDescription.ts delete mode 100644 frontend/openapi/model/restTaskDescriptionComponent.ts delete mode 100644 frontend/openapi/model/restTaskDescriptionTarget.ts delete mode 100644 frontend/openapi/model/restTaskDescriptionTargetItem.ts delete mode 100644 frontend/openapi/model/restTaskEndAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restTaskEndAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/restTaskModifiedAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restTaskModifiedAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/restTaskStartAuditLogEntry.ts delete mode 100644 frontend/openapi/model/restTeam.ts delete mode 100644 frontend/openapi/model/runInfo.ts delete mode 100644 frontend/openapi/model/runScoreSeries.ts delete mode 100644 frontend/openapi/model/runState.ts delete mode 100644 frontend/openapi/model/score.ts delete mode 100644 frontend/openapi/model/scoreOverview.ts delete mode 100644 frontend/openapi/model/scoreSeries.ts delete mode 100644 frontend/openapi/model/scoreSeriesPoint.ts delete mode 100644 frontend/openapi/model/sessionId.ts delete mode 100644 frontend/openapi/model/submission.ts delete mode 100644 frontend/openapi/model/submissionAuditLogEntry.ts delete mode 100644 frontend/openapi/model/submissionAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/submissionInfo.ts delete mode 100644 frontend/openapi/model/successStatus.ts delete mode 100644 frontend/openapi/model/taskDescription.ts delete mode 100644 frontend/openapi/model/taskDescriptionHint.ts delete mode 100644 frontend/openapi/model/taskDescriptionTarget.ts delete mode 100644 frontend/openapi/model/taskEndAuditLogEntry.ts delete mode 100644 frontend/openapi/model/taskEndAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/taskGroup.ts delete mode 100644 frontend/openapi/model/taskHint.ts delete mode 100644 frontend/openapi/model/taskInfo.ts delete mode 100644 frontend/openapi/model/taskModifiedAuditLogEntry.ts delete mode 100644 frontend/openapi/model/taskModifiedAuditLogEntryAllOf.ts delete mode 100644 frontend/openapi/model/taskRun.ts delete mode 100644 frontend/openapi/model/taskStartAuditLogEntry.ts delete mode 100644 frontend/openapi/model/taskTarget.ts delete mode 100644 frontend/openapi/model/taskType.ts delete mode 100644 frontend/openapi/model/team.ts delete mode 100644 frontend/openapi/model/teamInfo.ts delete mode 100644 frontend/openapi/model/temporalPoint.ts delete mode 100644 frontend/openapi/model/temporalRange.ts delete mode 100644 frontend/openapi/model/temporalSubmission.ts delete mode 100644 frontend/openapi/model/temporalSubmissionAllOf.ts delete mode 100644 frontend/openapi/model/textTaskDescriptionHint.ts delete mode 100644 frontend/openapi/model/textTaskDescriptionHintAllOf.ts delete mode 100644 frontend/openapi/model/uID.ts delete mode 100644 frontend/openapi/model/userDetails.ts delete mode 100644 frontend/openapi/model/userRequest.ts delete mode 100644 frontend/openapi/model/videoItem.ts delete mode 100644 frontend/openapi/model/videoItemAllOf.ts delete mode 100644 frontend/openapi/model/videoItemSegmentTaskDescriptionHint.ts delete mode 100644 frontend/openapi/model/videoItemSegmentTaskDescriptionHintAllOf.ts delete mode 100644 frontend/openapi/model/videoSegmentTarget.ts delete mode 100644 frontend/openapi/model/viewerInfo.ts delete mode 100644 frontend/openapi/ng-package.json delete mode 100644 frontend/openapi/package.json delete mode 100644 frontend/openapi/tsconfig.json delete mode 100644 frontend/openapi/variables.ts diff --git a/.gitignore b/.gitignore index 4775b55c6..80904c3d5 100644 --- a/.gitignore +++ b/.gitignore @@ -267,3 +267,6 @@ backend/ext/ffmpeg events/ backend/data* backend/statistics + +## Do not include the generated client bindings +frontend/openapi/ diff --git a/backend/build.gradle b/backend/build.gradle index 71d597873..05656e9ce 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -33,6 +33,9 @@ sourceCompatibility = 1.8 /// Default OpenAPI Specifications of dres def fullOAS = "http://localhost:8080/swagger-docs" +def clientOAS = "http://localhost:8080/client-oas" +def oasFile = "${project.projectDir}/../doc/oas.json" +def clientOasFile = "${project.projectDir}/../doc/oas-client.json" /// Provide ability to override with gradle command line input /// call gradle with -Poas="" to specify non-default OAS location if(project.hasProperty('oas')){ @@ -48,7 +51,7 @@ openApiGenerate { validateSpec = false // No validation please (as in command above) generatorName = 'typescript-angular' - inputSpec = "$buildDir/oas.json" + inputSpec = oasFile outputDir = file("${project.projectDir}/../frontend/openapi").toString() configOptions = [ npmName: '@dres-openapi/api', @@ -203,12 +206,22 @@ task deployFrontend(type: Copy) { into('src/main/resources/html') } -task downloadOAS(type: Download) { - def f = new File("$buildDir/oas.json") + +task generateOAS(type: Download) { + /* Requires DRES running on default port */ + // TODO Direct write to file instead of download + def f = new File(oasFile) src "$fullOAS" dest f } +task generateClientOAS(type: Download){ + /* Requires DRES running on default port */ + def f = new File(clientOasFile) + src "$clientOAS" + dest f +} + /** Custom tasks: FFmpeg Download and deployment. */ task downloadFFmpeg(type: Download) { @@ -285,4 +298,4 @@ distZip.dependsOn setupFFMpeg distTar.dependsOn jar distTar.dependsOn setupFFMpeg -tasks.openApiGenerate.dependsOn downloadOAS \ No newline at end of file +//tasks.openApiGenerate.dependsOn downloadOAS \ No newline at end of file diff --git a/doc/oas-client.json b/doc/oas-client.json new file mode 100644 index 000000000..211d84194 --- /dev/null +++ b/doc/oas-client.json @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json new file mode 100644 index 000000000..2b03e3aaa --- /dev/null +++ b/doc/oas.json @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionOptions":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionQueryComponentType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetType"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentType"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringType"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterType"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionOptions"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file diff --git a/frontend/openapi/.gitignore b/frontend/openapi/.gitignore deleted file mode 100644 index 149b57654..000000000 --- a/frontend/openapi/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -wwwroot/*.js -node_modules -typings -dist diff --git a/frontend/openapi/.openapi-generator-ignore b/frontend/openapi/.openapi-generator-ignore deleted file mode 100644 index 7484ee590..000000000 --- a/frontend/openapi/.openapi-generator-ignore +++ /dev/null @@ -1,23 +0,0 @@ -# OpenAPI Generator Ignore -# Generated by openapi-generator https://github.com/openapitools/openapi-generator - -# Use this file to prevent files from being overwritten by the generator. -# The patterns follow closely to .gitignore or .dockerignore. - -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: -#ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -#foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -#foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -#docs/*.md -# Then explicitly reverse the ignore rule for a single file: -#!docs/README.md diff --git a/frontend/openapi/.openapi-generator/FILES b/frontend/openapi/.openapi-generator/FILES deleted file mode 100644 index a4598cf3e..000000000 --- a/frontend/openapi/.openapi-generator/FILES +++ /dev/null @@ -1,98 +0,0 @@ -.gitignore -README.md -api.module.ts -api/api.ts -api/audit.service.ts -api/clientRunInfo.service.ts -api/collection.service.ts -api/competition.service.ts -api/competitionRun.service.ts -api/competitionRunAdmin.service.ts -api/competitionRunScores.service.ts -api/judgement.service.ts -api/log.service.ts -api/status.service.ts -api/submission.service.ts -api/user.service.ts -configuration.ts -encoder.ts -git_push.sh -index.ts -model/auditLogInfo.ts -model/clientRunInfo.ts -model/clientRunInfoList.ts -model/clientTaskInfo.ts -model/competitionCreate.ts -model/competitionOverview.ts -model/competitionStartMessage.ts -model/configuredOptionOptions.ts -model/configuredOptionQueryComponentType.ts -model/configuredOptionScoringType.ts -model/configuredOptionSubmissionFilterType.ts -model/configuredOptionTargetType.ts -model/contentElement.ts -model/currentTime.ts -model/errorStatus.ts -model/judgement.ts -model/judgementRequest.ts -model/judgementValidatorStatus.ts -model/judgementVote.ts -model/loginRequest.ts -model/models.ts -model/queryEvent.ts -model/queryEventLog.ts -model/queryResult.ts -model/queryResultLog.ts -model/restAuditLogEntry.ts -model/restCompetitionDescription.ts -model/restCompetitionEndAuditLogEntry.ts -model/restCompetitionEndAuditLogEntryAllOf.ts -model/restCompetitionStartAuditLogEntry.ts -model/restDetailedTeam.ts -model/restFullMediaCollection.ts -model/restJudgementAuditLogEntry.ts -model/restJudgementAuditLogEntryAllOf.ts -model/restLoginAuditLogEntry.ts -model/restLoginAuditLogEntryAllOf.ts -model/restLogoutAuditLogEntry.ts -model/restLogoutAuditLogEntryAllOf.ts -model/restMediaCollection.ts -model/restMediaItem.ts -model/restPrepareJudgementAuditLogEntry.ts -model/restPrepareJudgementAuditLogEntryAllOf.ts -model/restSubmissionAuditLogEntry.ts -model/restSubmissionAuditLogEntryAllOf.ts -model/restTaskDescription.ts -model/restTaskDescriptionComponent.ts -model/restTaskDescriptionTarget.ts -model/restTaskDescriptionTargetItem.ts -model/restTaskEndAuditLogEntry.ts -model/restTaskEndAuditLogEntryAllOf.ts -model/restTaskModifiedAuditLogEntry.ts -model/restTaskModifiedAuditLogEntryAllOf.ts -model/restTaskStartAuditLogEntry.ts -model/restTeam.ts -model/runInfo.ts -model/runState.ts -model/score.ts -model/scoreOverview.ts -model/scoreSeries.ts -model/scoreSeriesPoint.ts -model/sessionId.ts -model/submissionInfo.ts -model/successStatus.ts -model/taskGroup.ts -model/taskHint.ts -model/taskInfo.ts -model/taskTarget.ts -model/taskType.ts -model/teamInfo.ts -model/temporalPoint.ts -model/temporalRange.ts -model/userDetails.ts -model/userRequest.ts -model/viewerInfo.ts -ng-package.json -package.json -tsconfig.json -variables.ts diff --git a/frontend/openapi/.openapi-generator/VERSION b/frontend/openapi/.openapi-generator/VERSION deleted file mode 100644 index 28cbf7c0a..000000000 --- a/frontend/openapi/.openapi-generator/VERSION +++ /dev/null @@ -1 +0,0 @@ -5.0.0 \ No newline at end of file diff --git a/frontend/openapi/README.md b/frontend/openapi/README.md deleted file mode 100644 index fa956f61e..000000000 --- a/frontend/openapi/README.md +++ /dev/null @@ -1,203 +0,0 @@ -## @dres-openapi/api@1.0-SNAPSHOT.202103031232 - -### Building - -To install the required dependencies and to build the typescript sources run: -``` -npm install -npm run build -``` - -### publishing - -First build the package then run ```npm publish dist``` (don't forget to specify the `dist` folder!) - -### consuming - -Navigate to the folder of your consuming project and run one of next commands. - -_published:_ - -``` -npm install @dres-openapi/api@1.0-SNAPSHOT.202103031232 --save -``` - -_without publishing (not recommended):_ - -``` -npm install PATH_TO_GENERATED_PACKAGE/dist.tgz --save -``` - -_It's important to take the tgz file, otherwise you'll get trouble with links on windows_ - -_using `npm link`:_ - -In PATH_TO_GENERATED_PACKAGE/dist: -``` -npm link -``` - -In your project: -``` -npm link @dres-openapi/api -``` - -__Note for Windows users:__ The Angular CLI has troubles to use linked npm packages. -Please refer to this issue https://github.com/angular/angular-cli/issues/8284 for a solution / workaround. -Published packages are not effected by this issue. - - -#### General usage - -In your Angular project: - - -``` -// without configuring providers -import { ApiModule } from '@dres-openapi/api'; -import { HttpClientModule } from '@angular/common/http'; - -@NgModule({ - imports: [ - ApiModule, - // make sure to import the HttpClientModule in the AppModule only, - // see https://github.com/angular/angular/issues/20575 - HttpClientModule - ], - declarations: [ AppComponent ], - providers: [], - bootstrap: [ AppComponent ] -}) -export class AppModule {} -``` - -``` -// configuring providers -import { ApiModule, Configuration, ConfigurationParameters } from '@dres-openapi/api'; - -export function apiConfigFactory (): Configuration => { - const params: ConfigurationParameters = { - // set configuration parameters here. - } - return new Configuration(params); -} - -@NgModule({ - imports: [ ApiModule.forRoot(apiConfigFactory) ], - declarations: [ AppComponent ], - providers: [], - bootstrap: [ AppComponent ] -}) -export class AppModule {} -``` - -``` -// configuring providers with an authentication service that manages your access tokens -import { ApiModule, Configuration } from '@dres-openapi/api'; - -@NgModule({ - imports: [ ApiModule ], - declarations: [ AppComponent ], - providers: [ - { - provide: Configuration, - useFactory: (authService: AuthService) => new Configuration( - { - basePath: environment.apiUrl, - accessToken: authService.getAccessToken.bind(authService) - } - ), - deps: [AuthService], - multi: false - } - ], - bootstrap: [ AppComponent ] -}) -export class AppModule {} -``` - -``` -import { DefaultApi } from '@dres-openapi/api'; - -export class AppComponent { - constructor(private apiGateway: DefaultApi) { } -} -``` - -Note: The ApiModule is restricted to being instantiated once app wide. -This is to ensure that all services are treated as singletons. - -#### Using multiple OpenAPI files / APIs / ApiModules -In order to use multiple `ApiModules` generated from different OpenAPI files, -you can create an alias name when importing the modules -in order to avoid naming conflicts: -``` -import { ApiModule } from 'my-api-path'; -import { ApiModule as OtherApiModule } from 'my-other-api-path'; -import { HttpClientModule } from '@angular/common/http'; - -@NgModule({ - imports: [ - ApiModule, - OtherApiModule, - // make sure to import the HttpClientModule in the AppModule only, - // see https://github.com/angular/angular/issues/20575 - HttpClientModule - ] -}) -export class AppModule { - -} -``` - - -### Set service base path -If different than the generated base path, during app bootstrap, you can provide the base path to your service. - -``` -import { BASE_PATH } from '@dres-openapi/api'; - -bootstrap(AppComponent, [ - { provide: BASE_PATH, useValue: 'https://your-web-service.com' }, -]); -``` -or - -``` -import { BASE_PATH } from '@dres-openapi/api'; - -@NgModule({ - imports: [], - declarations: [ AppComponent ], - providers: [ provide: BASE_PATH, useValue: 'https://your-web-service.com' ], - bootstrap: [ AppComponent ] -}) -export class AppModule {} -``` - - -#### Using @angular/cli -First extend your `src/environments/*.ts` files by adding the corresponding base path: - -``` -export const environment = { - production: false, - API_BASE_PATH: 'http://127.0.0.1:8080' -}; -``` - -In the src/app/app.module.ts: -``` -import { BASE_PATH } from '@dres-openapi/api'; -import { environment } from '../environments/environment'; - -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ ], - providers: [{ provide: BASE_PATH, useValue: environment.API_BASE_PATH }], - bootstrap: [ AppComponent ] -}) -export class AppModule { } -``` diff --git a/frontend/openapi/api.module.ts b/frontend/openapi/api.module.ts deleted file mode 100644 index f654ad3bc..000000000 --- a/frontend/openapi/api.module.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { NgModule, ModuleWithProviders, SkipSelf, Optional } from '@angular/core'; -import { Configuration } from './configuration'; -import { HttpClient } from '@angular/common/http'; - -import { AuditService } from './api/audit.service'; -import { ClientRunInfoService } from './api/clientRunInfo.service'; -import { CollectionService } from './api/collection.service'; -import { CompetitionService } from './api/competition.service'; -import { CompetitionRunService } from './api/competitionRun.service'; -import { CompetitionRunAdminService } from './api/competitionRunAdmin.service'; -import { CompetitionRunScoresService } from './api/competitionRunScores.service'; -import { JudgementService } from './api/judgement.service'; -import { LogService } from './api/log.service'; -import { StatusService } from './api/status.service'; -import { SubmissionService } from './api/submission.service'; -import { UserService } from './api/user.service'; - -@NgModule({ - imports: [], - declarations: [], - exports: [], - providers: [] -}) -export class ApiModule { - public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders { - return { - ngModule: ApiModule, - providers: [ { provide: Configuration, useFactory: configurationFactory } ] - }; - } - - constructor( @Optional() @SkipSelf() parentModule: ApiModule, - @Optional() http: HttpClient) { - if (parentModule) { - throw new Error('ApiModule is already loaded. Import in your base AppModule only.'); - } - if (!http) { - throw new Error('You need to import the HttpClientModule in your AppModule! \n' + - 'See also https://github.com/angular/angular/issues/20575'); - } - } -} diff --git a/frontend/openapi/api/api.ts b/frontend/openapi/api/api.ts deleted file mode 100644 index 3b2a068db..000000000 --- a/frontend/openapi/api/api.ts +++ /dev/null @@ -1,25 +0,0 @@ -export * from './audit.service'; -import { AuditService } from './audit.service'; -export * from './clientRunInfo.service'; -import { ClientRunInfoService } from './clientRunInfo.service'; -export * from './collection.service'; -import { CollectionService } from './collection.service'; -export * from './competition.service'; -import { CompetitionService } from './competition.service'; -export * from './competitionRun.service'; -import { CompetitionRunService } from './competitionRun.service'; -export * from './competitionRunAdmin.service'; -import { CompetitionRunAdminService } from './competitionRunAdmin.service'; -export * from './competitionRunScores.service'; -import { CompetitionRunScoresService } from './competitionRunScores.service'; -export * from './judgement.service'; -import { JudgementService } from './judgement.service'; -export * from './log.service'; -import { LogService } from './log.service'; -export * from './status.service'; -import { StatusService } from './status.service'; -export * from './submission.service'; -import { SubmissionService } from './submission.service'; -export * from './user.service'; -import { UserService } from './user.service'; -export const APIS = [AuditService, ClientRunInfoService, CollectionService, CompetitionService, CompetitionRunService, CompetitionRunAdminService, CompetitionRunScoresService, JudgementService, LogService, StatusService, SubmissionService, UserService]; diff --git a/frontend/openapi/api/audit.service.ts b/frontend/openapi/api/audit.service.ts deleted file mode 100644 index d51019169..000000000 --- a/frontend/openapi/api/audit.service.ts +++ /dev/null @@ -1,228 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { AuditLogInfo } from '../model/models'; -import { ErrorStatus } from '../model/models'; -import { RestAuditLogEntry } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class AuditService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Gives information about the audit log. Namely size and latest timestamp of known audit logs - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiAuditInfo(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiAuditInfo(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiAuditInfo(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiAuditInfo(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/audit/info`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists all audit logs matching the query - * @param limit The maximum number of results. Default: 500 - * @param page The page index offset, relative to the limit - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiAuditListWithLimitWithPage(limit: number, page: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiAuditListWithLimitWithPage(limit: number, page: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiAuditListWithLimitWithPage(limit: number, page: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiAuditListWithLimitWithPage(limit: number, page: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (limit === null || limit === undefined) { - throw new Error('Required parameter limit was null or undefined when calling getApiAuditListWithLimitWithPage.'); - } - if (page === null || page === undefined) { - throw new Error('Required parameter page was null or undefined when calling getApiAuditListWithLimitWithPage.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/audit/list/${encodeURIComponent(String(limit))}/${encodeURIComponent(String(page))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists all audit logs matching the query - * @param since Timestamp of the earliest audit log to include - * @param upto Timestamp of the latest audit log to include. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiAuditLogsWithSinceWithUpto(since: number, upto: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiAuditLogsWithSinceWithUpto(since: number, upto: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiAuditLogsWithSinceWithUpto(since: number, upto: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiAuditLogsWithSinceWithUpto(since: number, upto: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (since === null || since === undefined) { - throw new Error('Required parameter since was null or undefined when calling getApiAuditLogsWithSinceWithUpto.'); - } - if (upto === null || upto === undefined) { - throw new Error('Required parameter upto was null or undefined when calling getApiAuditLogsWithSinceWithUpto.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/audit/logs/${encodeURIComponent(String(since))}/${encodeURIComponent(String(upto))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/clientRunInfo.service.ts b/frontend/openapi/api/clientRunInfo.service.ts deleted file mode 100644 index e03da7070..000000000 --- a/frontend/openapi/api/clientRunInfo.service.ts +++ /dev/null @@ -1,197 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { ClientRunInfoList } from '../model/models'; -import { ClientTaskInfo } from '../model/models'; -import { ErrorStatus } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class ClientRunInfoService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Returns an overview of the currently active task for a run - * @param runId - * @param session Session Token - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRuninfoCurrenttaskWithRunid(runId: string, session: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRuninfoCurrenttaskWithRunid(runId: string, session: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRuninfoCurrenttaskWithRunid(runId: string, session: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRuninfoCurrenttaskWithRunid(runId: string, session: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRuninfoCurrenttaskWithRunid.'); - } - if (session === null || session === undefined) { - throw new Error('Required parameter session was null or undefined when calling getApiRuninfoCurrenttaskWithRunid.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (session !== undefined && session !== null) { - queryParameters = this.addToHttpParams(queryParameters, - session, 'session'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/runInfo/currentTask/${encodeURIComponent(String(runId))}`, - { - params: queryParameters, - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists an overview of all competition runs visible to the current client - * @param session Session Token - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRuninfoList(session: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRuninfoList(session: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRuninfoList(session: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRuninfoList(session: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (session === null || session === undefined) { - throw new Error('Required parameter session was null or undefined when calling getApiRuninfoList.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (session !== undefined && session !== null) { - queryParameters = this.addToHttpParams(queryParameters, - session, 'session'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/runInfo/list`, - { - params: queryParameters, - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/collection.service.ts b/frontend/openapi/api/collection.service.ts deleted file mode 100644 index fd062ea47..000000000 --- a/frontend/openapi/api/collection.service.ts +++ /dev/null @@ -1,659 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { ErrorStatus } from '../model/models'; -import { RestFullMediaCollection } from '../model/models'; -import { RestMediaCollection } from '../model/models'; -import { RestMediaItem } from '../model/models'; -import { SuccessStatus } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class CollectionService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Deletes a media collection - * @param collectionId - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public deleteApiCollectionWithCollectionid(collectionId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public deleteApiCollectionWithCollectionid(collectionId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public deleteApiCollectionWithCollectionid(collectionId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public deleteApiCollectionWithCollectionid(collectionId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (collectionId === null || collectionId === undefined) { - throw new Error('Required parameter collectionId was null or undefined when calling deleteApiCollectionWithCollectionid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.delete(`${this.configuration.basePath}/api/collection/${encodeURIComponent(String(collectionId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Selects and returns a specific media item. - * @param mediaId Media item ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public deleteApiMediaitemWithMediaid(mediaId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public deleteApiMediaitemWithMediaid(mediaId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public deleteApiMediaitemWithMediaid(mediaId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public deleteApiMediaitemWithMediaid(mediaId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (mediaId === null || mediaId === undefined) { - throw new Error('Required parameter mediaId was null or undefined when calling deleteApiMediaitemWithMediaid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.delete(`${this.configuration.basePath}/api/mediaItem/${encodeURIComponent(String(mediaId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists all available media collections with basic information about their content. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCollectionList(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCollectionList(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCollectionList(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCollectionList(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/collection/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Shows the content of the specified media collection. - * @param collectionId Collection ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCollectionWithCollectionid(collectionId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiCollectionWithCollectionid(collectionId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCollectionWithCollectionid(collectionId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCollectionWithCollectionid(collectionId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (collectionId === null || collectionId === undefined) { - throw new Error('Required parameter collectionId was null or undefined when calling getApiCollectionWithCollectionid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/collection/${encodeURIComponent(String(collectionId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Selects and returns a random media item from a given media collection. - * @param collectionId Collection ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCollectionWithCollectionidRandom(collectionId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiCollectionWithCollectionidRandom(collectionId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCollectionWithCollectionidRandom(collectionId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCollectionWithCollectionidRandom(collectionId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (collectionId === null || collectionId === undefined) { - throw new Error('Required parameter collectionId was null or undefined when calling getApiCollectionWithCollectionidRandom.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/collection/${encodeURIComponent(String(collectionId))}/random`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists media items from a given media collection whose name start with the given string. - * @param collectionId Collection ID - * @param startsWith Name starts with - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCollectionWithCollectionidWithStartswith(collectionId: string, startsWith: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCollectionWithCollectionidWithStartswith(collectionId: string, startsWith: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCollectionWithCollectionidWithStartswith(collectionId: string, startsWith: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCollectionWithCollectionidWithStartswith(collectionId: string, startsWith: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (collectionId === null || collectionId === undefined) { - throw new Error('Required parameter collectionId was null or undefined when calling getApiCollectionWithCollectionidWithStartswith.'); - } - if (startsWith === null || startsWith === undefined) { - throw new Error('Required parameter startsWith was null or undefined when calling getApiCollectionWithCollectionidWithStartswith.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/collection/${encodeURIComponent(String(collectionId))}/${encodeURIComponent(String(startsWith))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists items from the external media collection whose name start with the given string. - * @param startsWith Name starts with. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiExternalWithStartswith(startsWith: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiExternalWithStartswith(startsWith: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiExternalWithStartswith(startsWith: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiExternalWithStartswith(startsWith: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (startsWith === null || startsWith === undefined) { - throw new Error('Required parameter startsWith was null or undefined when calling getApiExternalWithStartswith.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/external/${encodeURIComponent(String(startsWith))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Selects and returns a specific media item. - * @param mediaId Media item ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiMediaitemWithMediaid(mediaId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiMediaitemWithMediaid(mediaId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiMediaitemWithMediaid(mediaId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiMediaitemWithMediaid(mediaId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (mediaId === null || mediaId === undefined) { - throw new Error('Required parameter mediaId was null or undefined when calling getApiMediaitemWithMediaid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/mediaItem/${encodeURIComponent(String(mediaId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Updates a media collection - * @param restMediaCollection - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public patchApiCollection(restMediaCollection?: RestMediaCollection, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public patchApiCollection(restMediaCollection?: RestMediaCollection, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiCollection(restMediaCollection?: RestMediaCollection, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiCollection(restMediaCollection?: RestMediaCollection, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.patch(`${this.configuration.basePath}/api/collection`, - restMediaCollection, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Updates a Media Item to the specified Media Collection. - * @param restMediaItem - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public patchApiMediaitem(restMediaItem?: RestMediaItem, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public patchApiMediaitem(restMediaItem?: RestMediaItem, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiMediaitem(restMediaItem?: RestMediaItem, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiMediaitem(restMediaItem?: RestMediaItem, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.patch(`${this.configuration.basePath}/api/mediaItem`, - restMediaItem, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Adds a new media collection - * @param restMediaCollection - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiCollection(restMediaCollection?: RestMediaCollection, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiCollection(restMediaCollection?: RestMediaCollection, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiCollection(restMediaCollection?: RestMediaCollection, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiCollection(restMediaCollection?: RestMediaCollection, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/collection`, - restMediaCollection, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Adds a Media Item to the specified Media Collection. - * @param restMediaItem - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiMediaitem(restMediaItem?: RestMediaItem, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiMediaitem(restMediaItem?: RestMediaItem, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiMediaitem(restMediaItem?: RestMediaItem, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiMediaitem(restMediaItem?: RestMediaItem, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/mediaItem`, - restMediaItem, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/competition.service.ts b/frontend/openapi/api/competition.service.ts deleted file mode 100644 index 2d100124c..000000000 --- a/frontend/openapi/api/competition.service.ts +++ /dev/null @@ -1,464 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { CompetitionCreate } from '../model/models'; -import { CompetitionOverview } from '../model/models'; -import { ErrorStatus } from '../model/models'; -import { RestCompetitionDescription } from '../model/models'; -import { RestDetailedTeam } from '../model/models'; -import { RestTaskDescription } from '../model/models'; -import { RestTeam } from '../model/models'; -import { SuccessStatus } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class CompetitionService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Deletes the competition with the given competition ID. - * @param competitionId Competition ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public deleteApiCompetitionWithCompetitionid(competitionId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public deleteApiCompetitionWithCompetitionid(competitionId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public deleteApiCompetitionWithCompetitionid(competitionId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public deleteApiCompetitionWithCompetitionid(competitionId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (competitionId === null || competitionId === undefined) { - throw new Error('Required parameter competitionId was null or undefined when calling deleteApiCompetitionWithCompetitionid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.delete(`${this.configuration.basePath}/api/competition/${encodeURIComponent(String(competitionId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists an overview of all available competitions with basic information about their content. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCompetitionList(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCompetitionList(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCompetitionList(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCompetitionList(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/competition/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Loads the detailed definition of a specific competition. - * @param competitionId Competition ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCompetitionWithCompetitionid(competitionId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiCompetitionWithCompetitionid(competitionId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCompetitionWithCompetitionid(competitionId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCompetitionWithCompetitionid(competitionId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (competitionId === null || competitionId === undefined) { - throw new Error('Required parameter competitionId was null or undefined when calling getApiCompetitionWithCompetitionid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/competition/${encodeURIComponent(String(competitionId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists the Tasks of a specific competition. - * @param competitionId Competition ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCompetitionWithCompetitionidTaskList(competitionId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCompetitionWithCompetitionidTaskList(competitionId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCompetitionWithCompetitionidTaskList(competitionId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCompetitionWithCompetitionidTaskList(competitionId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (competitionId === null || competitionId === undefined) { - throw new Error('Required parameter competitionId was null or undefined when calling getApiCompetitionWithCompetitionidTaskList.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/competition/${encodeURIComponent(String(competitionId))}/task/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists the Teams of a specific competition. - * @param competitionId Competition ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCompetitionWithCompetitionidTeamList(competitionId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCompetitionWithCompetitionidTeamList(competitionId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCompetitionWithCompetitionidTeamList(competitionId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCompetitionWithCompetitionidTeamList(competitionId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (competitionId === null || competitionId === undefined) { - throw new Error('Required parameter competitionId was null or undefined when calling getApiCompetitionWithCompetitionidTeamList.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/competition/${encodeURIComponent(String(competitionId))}/team/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists the teams with their user details - * @param competitionId Competition ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiCompetitionWithCompetitionidTeamListDetails(competitionId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiCompetitionWithCompetitionidTeamListDetails(competitionId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCompetitionWithCompetitionidTeamListDetails(competitionId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiCompetitionWithCompetitionidTeamListDetails(competitionId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (competitionId === null || competitionId === undefined) { - throw new Error('Required parameter competitionId was null or undefined when calling getApiCompetitionWithCompetitionidTeamListDetails.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/competition/${encodeURIComponent(String(competitionId))}/team/list/details`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Updates an existing competition. - * @param restCompetitionDescription - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public patchApiCompetition(restCompetitionDescription?: RestCompetitionDescription, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public patchApiCompetition(restCompetitionDescription?: RestCompetitionDescription, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiCompetition(restCompetitionDescription?: RestCompetitionDescription, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiCompetition(restCompetitionDescription?: RestCompetitionDescription, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.patch(`${this.configuration.basePath}/api/competition`, - restCompetitionDescription, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Creates a new competition. - * @param competitionCreate - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiCompetition(competitionCreate?: CompetitionCreate, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiCompetition(competitionCreate?: CompetitionCreate, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiCompetition(competitionCreate?: CompetitionCreate, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiCompetition(competitionCreate?: CompetitionCreate, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/competition`, - competitionCreate, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/competitionRun.service.ts b/frontend/openapi/api/competitionRun.service.ts deleted file mode 100644 index a23e97515..000000000 --- a/frontend/openapi/api/competitionRun.service.ts +++ /dev/null @@ -1,543 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { ErrorStatus } from '../model/models'; -import { RunInfo } from '../model/models'; -import { RunState } from '../model/models'; -import { SubmissionInfo } from '../model/models'; -import { TaskHint } from '../model/models'; -import { TaskInfo } from '../model/models'; -import { TaskTarget } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class CompetitionRunService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Lists an overview of all competition runs visible to the current user - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunInfoList(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunInfoList(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunInfoList(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunInfoList(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/run/info/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns a specific competition run. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunInfoWithRunid(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRunInfoWithRunid(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunInfoWithRunid(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunInfoWithRunid(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunInfoWithRunid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/run/info/${encodeURIComponent(String(runId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists an overview of all competition runs visible to the current user - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunStateList(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunStateList(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunStateList(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunStateList(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/run/state/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the state of a specific competition run. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunStateWithRunid(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRunStateWithRunid(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunStateWithRunid(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunStateWithRunid(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunStateWithRunid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/run/state/${encodeURIComponent(String(runId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the task hint for the current task run (i.e. the one that is currently selected). - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidHint(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRunWithRunidHint(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidHint(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidHint(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidHint.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/hint`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the submissions for the current task run, if it is either running or has just ended. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidSubmissions(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidSubmissions(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunWithRunidSubmissions(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunWithRunidSubmissions(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidSubmissions.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/submissions`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended. - * @param runId Competition Run ID - * @param timestamp Minimum Timestamp of returned submissions. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidSubmissionsAfterWithTimestamp(runId: string, timestamp: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidSubmissionsAfterWithTimestamp(runId: string, timestamp: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunWithRunidSubmissionsAfterWithTimestamp(runId: string, timestamp: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunWithRunidSubmissionsAfterWithTimestamp(runId: string, timestamp: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidSubmissionsAfterWithTimestamp.'); - } - if (timestamp === null || timestamp === undefined) { - throw new Error('Required parameter timestamp was null or undefined when calling getApiRunWithRunidSubmissionsAfterWithTimestamp.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/submissions/after/${encodeURIComponent(String(timestamp))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the task target for the current task run (i.e. the one that is currently selected). - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidTarget(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRunWithRunidTarget(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidTarget(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidTarget(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidTarget.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/target`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the information for the current task (i.e. the one that is currently selected). - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidTask(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRunWithRunidTask(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidTask(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidTask(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidTask.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/task`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the submissions of a specific task run, regardless of whether it is currently running or has ended. - * @param runId Competition Run ID - * @param taskId Task run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidTaskWithTaskidSubmissions(runId: string, taskId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidTaskWithTaskidSubmissions(runId: string, taskId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunWithRunidTaskWithTaskidSubmissions(runId: string, taskId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunWithRunidTaskWithTaskidSubmissions(runId: string, taskId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidTaskWithTaskidSubmissions.'); - } - if (taskId === null || taskId === undefined) { - throw new Error('Required parameter taskId was null or undefined when calling getApiRunWithRunidTaskWithTaskidSubmissions.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/task/${encodeURIComponent(String(taskId))}/submissions`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/competitionRunAdmin.service.ts b/frontend/openapi/api/competitionRunAdmin.service.ts deleted file mode 100644 index 2ee133e5e..000000000 --- a/frontend/openapi/api/competitionRunAdmin.service.ts +++ /dev/null @@ -1,719 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { CompetitionStartMessage } from '../model/models'; -import { ErrorStatus } from '../model/models'; -import { SubmissionInfo } from '../model/models'; -import { SuccessStatus } from '../model/models'; -import { ViewerInfo } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class CompetitionRunAdminService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Lists all submissions for a given task and run - * @param runId Competition Run ID - * @param taskId Task ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunAdminWithRunidSubmissionsListWithTaskid(runId: string, taskId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunAdminWithRunidSubmissionsListWithTaskid(runId: string, taskId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunAdminWithRunidSubmissionsListWithTaskid(runId: string, taskId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunAdminWithRunidSubmissionsListWithTaskid(runId: string, taskId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunAdminWithRunidSubmissionsListWithTaskid.'); - } - if (taskId === null || taskId === undefined) { - throw new Error('Required parameter taskId was null or undefined when calling getApiRunAdminWithRunidSubmissionsListWithTaskid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/submissions/list/${encodeURIComponent(String(taskId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists all registered viewers for a competition run. This is a method for admins. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunAdminWithRunidViewerList(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunAdminWithRunidViewerList(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunAdminWithRunidViewerList(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunAdminWithRunidViewerList(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunAdminWithRunidViewerList.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/viewer/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists all submissions for a given task and run - * @param runId Competition Run ID - * @param submissionInfo - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public patchApiRunAdminWithRunidSubmissionsOverride(runId: string, submissionInfo?: SubmissionInfo, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public patchApiRunAdminWithRunidSubmissionsOverride(runId: string, submissionInfo?: SubmissionInfo, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiRunAdminWithRunidSubmissionsOverride(runId: string, submissionInfo?: SubmissionInfo, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiRunAdminWithRunidSubmissionsOverride(runId: string, submissionInfo?: SubmissionInfo, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling patchApiRunAdminWithRunidSubmissionsOverride.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.patch(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/submissions/override`, - submissionInfo, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Creates a new competition run from an existing competition - * @param competitionStartMessage - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminCreate(competitionStartMessage?: CompetitionStartMessage, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminCreate(competitionStartMessage?: CompetitionStartMessage, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminCreate(competitionStartMessage?: CompetitionStartMessage, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminCreate(competitionStartMessage?: CompetitionStartMessage, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/create`, - competitionStartMessage, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Adjusts the duration of a running task run. This is a method for admins. - * @param runId Competition Run ID - * @param duration Duration to add. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidAdjustWithDuration(runId: string, duration: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidAdjustWithDuration(runId: string, duration: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidAdjustWithDuration(runId: string, duration: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidAdjustWithDuration(runId: string, duration: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidAdjustWithDuration.'); - } - if (duration === null || duration === undefined) { - throw new Error('Required parameter duration was null or undefined when calling postApiRunAdminWithRunidAdjustWithDuration.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/adjust/${encodeURIComponent(String(duration))}`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Starts a competition run. This is a method for admins. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidStart(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidStart(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidStart(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidStart(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidStart.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/start`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Aborts the currently running task run. This is a method for admins. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidTaskAbort(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidTaskAbort(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskAbort(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskAbort(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidTaskAbort.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/task/abort`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Moves to and selects the next task. This is a method for admins. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidTaskNext(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidTaskNext(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskNext(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskNext(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidTaskNext.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/task/next`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Moves to and selects the previous task. This is a method for admins. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidTaskPrevious(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidTaskPrevious(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskPrevious(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskPrevious(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidTaskPrevious.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/task/previous`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Starts the currently active task as a new task run. This is a method for admins. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidTaskStart(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidTaskStart(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskStart(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskStart(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidTaskStart.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/task/start`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Moves to and selects the specified task. This is a method for admins. - * @param runId Competition run ID - * @param idx Index of the task to switch to. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidTaskSwitchWithIdx(runId: string, idx: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidTaskSwitchWithIdx(runId: string, idx: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskSwitchWithIdx(runId: string, idx: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTaskSwitchWithIdx(runId: string, idx: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidTaskSwitchWithIdx.'); - } - if (idx === null || idx === undefined) { - throw new Error('Required parameter idx was null or undefined when calling postApiRunAdminWithRunidTaskSwitchWithIdx.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/task/switch/${encodeURIComponent(String(idx))}`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Terminates a competition run. This is a method for admins. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidTerminate(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidTerminate(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTerminate(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidTerminate(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidTerminate.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/terminate`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Forces a viewer with the given viewer ID into the READY state. This is a method for admins. - * @param runId Competition Run ID - * @param viewerId Viewer ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunAdminWithRunidViewersWithVieweridForce(runId: string, viewerId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunAdminWithRunidViewersWithVieweridForce(runId: string, viewerId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidViewersWithVieweridForce(runId: string, viewerId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunAdminWithRunidViewersWithVieweridForce(runId: string, viewerId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunAdminWithRunidViewersWithVieweridForce.'); - } - if (viewerId === null || viewerId === undefined) { - throw new Error('Required parameter viewerId was null or undefined when calling postApiRunAdminWithRunidViewersWithVieweridForce.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/admin/${encodeURIComponent(String(runId))}/viewers/${encodeURIComponent(String(viewerId))}/force`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/competitionRunScores.service.ts b/frontend/openapi/api/competitionRunScores.service.ts deleted file mode 100644 index 07bfaf060..000000000 --- a/frontend/openapi/api/competitionRunScores.service.ts +++ /dev/null @@ -1,322 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { ErrorStatus } from '../model/models'; -import { ScoreOverview } from '../model/models'; -import { ScoreSeries } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class CompetitionRunScoresService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Returns the score overviews of a specific competition run. - * @param runId Competition Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiScoreRunWithRunid(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiScoreRunWithRunid(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiScoreRunWithRunid(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiScoreRunWithRunid(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiScoreRunWithRunid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/score/run/${encodeURIComponent(String(runId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the overviews of all score boards for the current task run, if it is either running or has just ended. - * @param runId Competition run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiScoreRunWithRunidCurrent(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiScoreRunWithRunidCurrent(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiScoreRunWithRunidCurrent(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiScoreRunWithRunidCurrent(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiScoreRunWithRunidCurrent.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/score/run/${encodeURIComponent(String(runId))}/current`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns the overviews of all score boards for the specified task run. - * @param runId Competition run ID - * @param taskId Task run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiScoreRunWithRunidHistoryWithTaskid(runId: string, taskId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiScoreRunWithRunidHistoryWithTaskid(runId: string, taskId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiScoreRunWithRunidHistoryWithTaskid(runId: string, taskId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiScoreRunWithRunidHistoryWithTaskid(runId: string, taskId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiScoreRunWithRunidHistoryWithTaskid.'); - } - if (taskId === null || taskId === undefined) { - throw new Error('Required parameter taskId was null or undefined when calling getApiScoreRunWithRunidHistoryWithTaskid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/score/run/${encodeURIComponent(String(runId))}/history/${encodeURIComponent(String(taskId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns a list of available scoreboard names for the given run. - * @param runId ID of the competition run. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiScoreRunWithRunidScoreboards(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiScoreRunWithRunidScoreboards(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiScoreRunWithRunidScoreboards(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiScoreRunWithRunidScoreboards(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiScoreRunWithRunidScoreboards.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/score/run/${encodeURIComponent(String(runId))}/scoreboards`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns a time series for a given run and scoreboard. - * @param runId ID of the competition run. - * @param scoreboard Name of the scoreboard to return the time series for. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiScoreRunWithRunidSeriesWithScoreboard(runId: string, scoreboard: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiScoreRunWithRunidSeriesWithScoreboard(runId: string, scoreboard: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiScoreRunWithRunidSeriesWithScoreboard(runId: string, scoreboard: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiScoreRunWithRunidSeriesWithScoreboard(runId: string, scoreboard: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiScoreRunWithRunidSeriesWithScoreboard.'); - } - if (scoreboard === null || scoreboard === undefined) { - throw new Error('Required parameter scoreboard was null or undefined when calling getApiScoreRunWithRunidSeriesWithScoreboard.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/score/run/${encodeURIComponent(String(runId))}/series/${encodeURIComponent(String(scoreboard))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/default.service.ts b/frontend/openapi/api/default.service.ts deleted file mode 100644 index bb02ecb4a..000000000 --- a/frontend/openapi/api/default.service.ts +++ /dev/null @@ -1,131 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class DefaultService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Post api run with runid judge vote - * @param runId - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunWithRunidJudgeVote(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined}): Observable; - public postApiRunWithRunidJudgeVote(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined}): Observable>; - public postApiRunWithRunidJudgeVote(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined}): Observable>; - public postApiRunWithRunidJudgeVote(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunWithRunidJudgeVote.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/judge/vote`, - null, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/judgement.service.ts b/frontend/openapi/api/judgement.service.ts deleted file mode 100644 index d7dc33956..000000000 --- a/frontend/openapi/api/judgement.service.ts +++ /dev/null @@ -1,339 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { ErrorStatus } from '../model/models'; -import { Judgement } from '../model/models'; -import { JudgementRequest } from '../model/models'; -import { JudgementValidatorStatus } from '../model/models'; -import { JudgementVote } from '../model/models'; -import { SuccessStatus } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class JudgementService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Gets the next open Submission to be judged. - * @param runId Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidJudgeNext(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRunWithRunidJudgeNext(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidJudgeNext(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidJudgeNext(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidJudgeNext.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/judge/next`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Gets the status of all judgement validators. - * @param runId Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidJudgeStatus(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidJudgeStatus(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunWithRunidJudgeStatus(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiRunWithRunidJudgeStatus(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidJudgeStatus.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/judge/status`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Gets the next open Submission to voted on. - * @param runId Run ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiRunWithRunidVoteNext(runId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiRunWithRunidVoteNext(runId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidVoteNext(runId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiRunWithRunidVoteNext(runId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling getApiRunWithRunidVoteNext.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/vote/next`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns a Judgement. - * @param runId Run ID - * @param judgement - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunWithRunidJudge(runId: string, judgement?: Judgement, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunWithRunidJudge(runId: string, judgement?: Judgement, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunWithRunidJudge(runId: string, judgement?: Judgement, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunWithRunidJudge(runId: string, judgement?: Judgement, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunWithRunidJudge.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/judge`, - judgement, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns a Vote. - * @param runId Run ID - * @param judgementVote - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiRunWithRunidJudgeVote(runId: string, judgementVote?: JudgementVote, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiRunWithRunidJudgeVote(runId: string, judgementVote?: JudgementVote, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunWithRunidJudgeVote(runId: string, judgementVote?: JudgementVote, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiRunWithRunidJudgeVote(runId: string, judgementVote?: JudgementVote, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (runId === null || runId === undefined) { - throw new Error('Required parameter runId was null or undefined when calling postApiRunWithRunidJudgeVote.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/run/${encodeURIComponent(String(runId))}/judge/vote`, - judgementVote, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/log.service.ts b/frontend/openapi/api/log.service.ts deleted file mode 100644 index 5bd0fa7ae..000000000 --- a/frontend/openapi/api/log.service.ts +++ /dev/null @@ -1,216 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { ErrorStatus } from '../model/models'; -import { QueryEventLog } from '../model/models'; -import { QueryResultLog } from '../model/models'; -import { SuccessStatus } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class LogService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Accepts query logs from participants - * @param session Session Token - * @param queryEventLog - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postLogQuery(session: string, queryEventLog?: QueryEventLog, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postLogQuery(session: string, queryEventLog?: QueryEventLog, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postLogQuery(session: string, queryEventLog?: QueryEventLog, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postLogQuery(session: string, queryEventLog?: QueryEventLog, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (session === null || session === undefined) { - throw new Error('Required parameter session was null or undefined when calling postLogQuery.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (session !== undefined && session !== null) { - queryParameters = this.addToHttpParams(queryParameters, - session, 'session'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/log/query`, - queryEventLog, - { - params: queryParameters, - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Accepts result logs from participants - * @param session Session Token - * @param queryResultLog - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postLogResult(session: string, queryResultLog?: QueryResultLog, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postLogResult(session: string, queryResultLog?: QueryResultLog, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postLogResult(session: string, queryResultLog?: QueryResultLog, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postLogResult(session: string, queryResultLog?: QueryResultLog, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (session === null || session === undefined) { - throw new Error('Required parameter session was null or undefined when calling postLogResult.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (session !== undefined && session !== null) { - queryParameters = this.addToHttpParams(queryParameters, - session, 'session'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/log/result`, - queryResultLog, - { - params: queryParameters, - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/status.service.ts b/frontend/openapi/api/status.service.ts deleted file mode 100644 index e3c877150..000000000 --- a/frontend/openapi/api/status.service.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { CurrentTime } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class StatusService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Returns the current time on the server. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiStatusTime(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiStatusTime(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiStatusTime(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiStatusTime(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/status/time`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/submission.service.ts b/frontend/openapi/api/submission.service.ts deleted file mode 100644 index d57b9e186..000000000 --- a/frontend/openapi/api/submission.service.ts +++ /dev/null @@ -1,165 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { ErrorStatus } from '../model/models'; -import { SuccessStatus } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class SubmissionService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Endpoint to accept submissions - * @param session Session Token - * @param collection Collection identifier. Optional, in which case the default collection for the run will be considered. - * @param item Identifier for the actual media object or media file. - * @param frame Frame number for media with temporal progression (e.g. video). - * @param shot Shot number for media with temporal progression (e.g. video). - * @param timecode Timecode for media with temporal progression (e.g. video). - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getSubmit(session: string, collection?: string, item?: string, frame?: number, shot?: number, timecode?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getSubmit(session: string, collection?: string, item?: string, frame?: number, shot?: number, timecode?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getSubmit(session: string, collection?: string, item?: string, frame?: number, shot?: number, timecode?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getSubmit(session: string, collection?: string, item?: string, frame?: number, shot?: number, timecode?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (session === null || session === undefined) { - throw new Error('Required parameter session was null or undefined when calling getSubmit.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (collection !== undefined && collection !== null) { - queryParameters = this.addToHttpParams(queryParameters, - collection, 'collection'); - } - if (item !== undefined && item !== null) { - queryParameters = this.addToHttpParams(queryParameters, - item, 'item'); - } - if (frame !== undefined && frame !== null) { - queryParameters = this.addToHttpParams(queryParameters, - frame, 'frame'); - } - if (shot !== undefined && shot !== null) { - queryParameters = this.addToHttpParams(queryParameters, - shot, 'shot'); - } - if (timecode !== undefined && timecode !== null) { - queryParameters = this.addToHttpParams(queryParameters, - timecode, 'timecode'); - } - if (session !== undefined && session !== null) { - queryParameters = this.addToHttpParams(queryParameters, - session, 'session'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/submit`, - { - params: queryParameters, - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/api/user.service.ts b/frontend/openapi/api/user.service.ts deleted file mode 100644 index 1bfb259f6..000000000 --- a/frontend/openapi/api/user.service.ts +++ /dev/null @@ -1,569 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { ErrorStatus } from '../model/models'; -import { LoginRequest } from '../model/models'; -import { SessionId } from '../model/models'; -import { SuccessStatus } from '../model/models'; -import { UserDetails } from '../model/models'; -import { UserRequest } from '../model/models'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class UserService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { - if (typeof value === "object" && value instanceof Date === false) { - httpParams = this.addToHttpParamsRecursive(httpParams, value); - } else { - httpParams = this.addToHttpParamsRecursive(httpParams, value, key); - } - return httpParams; - } - - private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { - if (value == null) { - return httpParams; - } - - if (typeof value === "object") { - if (Array.isArray(value)) { - (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); - } else if (value instanceof Date) { - if (key != null) { - httpParams = httpParams.append(key, - (value as Date).toISOString().substr(0, 10)); - } else { - throw Error("key may not be null if value is Date"); - } - } else { - Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( - httpParams, value[k], key != null ? `${key}.${k}` : k)); - } - } else if (key != null) { - httpParams = httpParams.append(key, value); - } else { - throw Error("key may not be null if value is not object or array"); - } - return httpParams; - } - - /** - * Deletes the specified user. Requires ADMIN privileges - * @param userId User ID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public deleteApiUserWithUserid(userId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public deleteApiUserWithUserid(userId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public deleteApiUserWithUserid(userId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public deleteApiUserWithUserid(userId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (userId === null || userId === undefined) { - throw new Error('Required parameter userId was null or undefined when calling deleteApiUserWithUserid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.delete(`${this.configuration.basePath}/api/user/${encodeURIComponent(String(userId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Clears all user roles of the current session. - * @param session Session Token - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiLogout(session: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiLogout(session: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiLogout(session: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiLogout(session: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (session === null || session === undefined) { - throw new Error('Required parameter session was null or undefined when calling getApiLogout.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (session !== undefined && session !== null) { - queryParameters = this.addToHttpParams(queryParameters, - session, 'session'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/logout`, - { - params: queryParameters, - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Get information about the current user. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiUser(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiUser(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiUser(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiUser(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/user`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Lists all available users. - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiUserList(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiUserList(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiUserList(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiUserList(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/user/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Get current sessionId - * @param session Session Token - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiUserSession(session: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiUserSession(session: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiUserSession(session: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiUserSession(session: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (session === null || session === undefined) { - throw new Error('Required parameter session was null or undefined when calling getApiUserSession.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (session !== undefined && session !== null) { - queryParameters = this.addToHttpParams(queryParameters, - session, 'session'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/user/session`, - { - params: queryParameters, - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Get details of all current user sessions - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiUserSessionActiveList(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiUserSessionActiveList(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiUserSessionActiveList(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; - public getApiUserSessionActiveList(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get>(`${this.configuration.basePath}/api/user/session/active/list`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Gets details of the user with the given id - * @param userId User\'s UID - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public getApiUserWithUserid(userId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public getApiUserWithUserid(userId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiUserWithUserid(userId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public getApiUserWithUserid(userId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (userId === null || userId === undefined) { - throw new Error('Required parameter userId was null or undefined when calling getApiUserWithUserid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.get(`${this.configuration.basePath}/api/user/${encodeURIComponent(String(userId))}`, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone - * @param userId User ID - * @param userRequest - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public patchApiUserWithUserid(userId: string, userRequest?: UserRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public patchApiUserWithUserid(userId: string, userRequest?: UserRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiUserWithUserid(userId: string, userRequest?: UserRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public patchApiUserWithUserid(userId: string, userRequest?: UserRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (userId === null || userId === undefined) { - throw new Error('Required parameter userId was null or undefined when calling patchApiUserWithUserid.'); - } - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.patch(`${this.configuration.basePath}/api/user/${encodeURIComponent(String(userId))}`, - userRequest, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Sets roles for session based on user account and returns a session cookie. - * @param loginRequest - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiLogin(loginRequest?: LoginRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiLogin(loginRequest?: LoginRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiLogin(loginRequest?: LoginRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiLogin(loginRequest?: LoginRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/login`, - loginRequest, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Creates a new user, if the username is not already taken. Requires ADMIN privileges - * @param userRequest - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public postApiUser(userRequest?: UserRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public postApiUser(userRequest?: UserRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiUser(userRequest?: UserRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public postApiUser(userRequest?: UserRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - - let headers = this.defaultHeaders; - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/api/user`, - userRequest, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/frontend/openapi/configuration.ts b/frontend/openapi/configuration.ts deleted file mode 100644 index 4ca354692..000000000 --- a/frontend/openapi/configuration.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { HttpParameterCodec } from '@angular/common/http'; - -export interface ConfigurationParameters { - /** - * @deprecated Since 5.0. Use credentials instead - */ - apiKeys?: {[ key: string ]: string}; - username?: string; - password?: string; - /** - * @deprecated Since 5.0. Use credentials instead - */ - accessToken?: string | (() => string); - basePath?: string; - withCredentials?: boolean; - encoder?: HttpParameterCodec; - /** - * The keys are the names in the securitySchemes section of the OpenAPI - * document. They should map to the value used for authentication - * minus any standard prefixes such as 'Basic' or 'Bearer'. - */ - credentials?: {[ key: string ]: string | (() => string | undefined)}; -} - -export class Configuration { - /** - * @deprecated Since 5.0. Use credentials instead - */ - apiKeys?: {[ key: string ]: string}; - username?: string; - password?: string; - /** - * @deprecated Since 5.0. Use credentials instead - */ - accessToken?: string | (() => string); - basePath?: string; - withCredentials?: boolean; - encoder?: HttpParameterCodec; - /** - * The keys are the names in the securitySchemes section of the OpenAPI - * document. They should map to the value used for authentication - * minus any standard prefixes such as 'Basic' or 'Bearer'. - */ - credentials: {[ key: string ]: string | (() => string | undefined)}; - - constructor(configurationParameters: ConfigurationParameters = {}) { - this.apiKeys = configurationParameters.apiKeys; - this.username = configurationParameters.username; - this.password = configurationParameters.password; - this.accessToken = configurationParameters.accessToken; - this.basePath = configurationParameters.basePath; - this.withCredentials = configurationParameters.withCredentials; - this.encoder = configurationParameters.encoder; - if (configurationParameters.credentials) { - this.credentials = configurationParameters.credentials; - } - else { - this.credentials = {}; - } - } - - /** - * Select the correct content-type to use for a request. - * Uses {@link Configuration#isJsonMime} to determine the correct content-type. - * If no content type is found return the first found type if the contentTypes is not empty - * @param contentTypes - the array of content types that are available for selection - * @returns the selected content-type or undefined if no selection could be made. - */ - public selectHeaderContentType (contentTypes: string[]): string | undefined { - if (contentTypes.length === 0) { - return undefined; - } - - const type = contentTypes.find((x: string) => this.isJsonMime(x)); - if (type === undefined) { - return contentTypes[0]; - } - return type; - } - - /** - * Select the correct accept content-type to use for a request. - * Uses {@link Configuration#isJsonMime} to determine the correct accept content-type. - * If no content type is found return the first found type if the contentTypes is not empty - * @param accepts - the array of content types that are available for selection. - * @returns the selected content-type or undefined if no selection could be made. - */ - public selectHeaderAccept(accepts: string[]): string | undefined { - if (accepts.length === 0) { - return undefined; - } - - const type = accepts.find((x: string) => this.isJsonMime(x)); - if (type === undefined) { - return accepts[0]; - } - return type; - } - - /** - * Check if the given MIME is a JSON MIME. - * JSON MIME examples: - * application/json - * application/json; charset=UTF8 - * APPLICATION/JSON - * application/vnd.company+json - * @param mime - MIME (Multipurpose Internet Mail Extensions) - * @return True if the given MIME is JSON, false otherwise. - */ - public isJsonMime(mime: string): boolean { - const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); - return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); - } - - public lookupCredential(key: string): string | undefined { - const value = this.credentials[key]; - return typeof value === 'function' - ? value() - : value; - } -} diff --git a/frontend/openapi/encoder.ts b/frontend/openapi/encoder.ts deleted file mode 100644 index 138c4d5cf..000000000 --- a/frontend/openapi/encoder.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { HttpParameterCodec } from '@angular/common/http'; - -/** - * Custom HttpParameterCodec - * Workaround for https://github.com/angular/angular/issues/18261 - */ -export class CustomHttpParameterCodec implements HttpParameterCodec { - encodeKey(k: string): string { - return encodeURIComponent(k); - } - encodeValue(v: string): string { - return encodeURIComponent(v); - } - decodeKey(k: string): string { - return decodeURIComponent(k); - } - decodeValue(v: string): string { - return decodeURIComponent(v); - } -} diff --git a/frontend/openapi/git_push.sh b/frontend/openapi/git_push.sh deleted file mode 100644 index ced3be2b0..000000000 --- a/frontend/openapi/git_push.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/sh -# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ -# -# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com" - -git_user_id=$1 -git_repo_id=$2 -release_note=$3 -git_host=$4 - -if [ "$git_host" = "" ]; then - git_host="github.com" - echo "[INFO] No command line input provided. Set \$git_host to $git_host" -fi - -if [ "$git_user_id" = "" ]; then - git_user_id="GIT_USER_ID" - echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" -fi - -if [ "$git_repo_id" = "" ]; then - git_repo_id="GIT_REPO_ID" - echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" -fi - -if [ "$release_note" = "" ]; then - release_note="Minor update" - echo "[INFO] No command line input provided. Set \$release_note to $release_note" -fi - -# Initialize the local directory as a Git repository -git init - -# Adds the files in the local repository and stages them for commit. -git add . - -# Commits the tracked changes and prepares them to be pushed to a remote repository. -git commit -m "$release_note" - -# Sets the new remote -git_remote=`git remote` -if [ "$git_remote" = "" ]; then # git remote not defined - - if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." - git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git - else - git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git - fi - -fi - -git pull origin master - -# Pushes (Forces) the changes in the local repository up to the remote repository -echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" -git push origin master 2>&1 | grep -v 'To https' - diff --git a/frontend/openapi/index.ts b/frontend/openapi/index.ts deleted file mode 100644 index c312b70fa..000000000 --- a/frontend/openapi/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './api/api'; -export * from './model/models'; -export * from './variables'; -export * from './configuration'; -export * from './api.module'; \ No newline at end of file diff --git a/frontend/openapi/model/auditLogEntry.ts b/frontend/openapi/model/auditLogEntry.ts deleted file mode 100644 index 097cd37d8..000000000 --- a/frontend/openapi/model/auditLogEntry.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface AuditLogEntry { - type: AuditLogEntry.TypeEnum; - timestamp: number; - timestamp$backend?: number; - id_1rSl5jE: string; - id_Fwp7XY8?: string; -} -export namespace AuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITIONSTART: 'COMPETITION_START' as TypeEnum, - COMPETITIONEND: 'COMPETITION_END' as TypeEnum, - TASKSTART: 'TASK_START' as TypeEnum, - TASKMODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASKEND: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; -} - - diff --git a/frontend/openapi/model/auditLogInfo.ts b/frontend/openapi/model/auditLogInfo.ts deleted file mode 100644 index 11671c543..000000000 --- a/frontend/openapi/model/auditLogInfo.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface AuditLogInfo { - timestamp: number; - size: number; - latest: number; -} - diff --git a/frontend/openapi/model/clientRunInfo.ts b/frontend/openapi/model/clientRunInfo.ts deleted file mode 100644 index 78b8f7243..000000000 --- a/frontend/openapi/model/clientRunInfo.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ClientRunInfo { - id: string; - name: string; - description?: string; - status: ClientRunInfo.StatusEnum; -} -export namespace ClientRunInfo { - export type StatusEnum = 'CREATED' | 'ACTIVE' | 'PREPARING_TASK' | 'RUNNING_TASK' | 'TASK_ENDED' | 'TERMINATED'; - export const StatusEnum = { - CREATED: 'CREATED' as StatusEnum, - ACTIVE: 'ACTIVE' as StatusEnum, - PREPARING_TASK: 'PREPARING_TASK' as StatusEnum, - RUNNING_TASK: 'RUNNING_TASK' as StatusEnum, - TASK_ENDED: 'TASK_ENDED' as StatusEnum, - TERMINATED: 'TERMINATED' as StatusEnum - }; -} - - diff --git a/frontend/openapi/model/clientRunInfoList.ts b/frontend/openapi/model/clientRunInfoList.ts deleted file mode 100644 index 8f84cbdc5..000000000 --- a/frontend/openapi/model/clientRunInfoList.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ClientRunInfo } from './clientRunInfo'; - - -export interface ClientRunInfoList { - runs: Array; -} - diff --git a/frontend/openapi/model/clientTaskInfo.ts b/frontend/openapi/model/clientTaskInfo.ts deleted file mode 100644 index 55ed7b69b..000000000 --- a/frontend/openapi/model/clientTaskInfo.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ClientTaskInfo { - id: string; - name: string; - taskGroup: string; - remainingTime: number; - running: boolean; -} - diff --git a/frontend/openapi/model/competitionCreate.ts b/frontend/openapi/model/competitionCreate.ts deleted file mode 100644 index c1c427518..000000000 --- a/frontend/openapi/model/competitionCreate.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface CompetitionCreate { - name: string; - description: string; - participantsCanView: boolean; -} - diff --git a/frontend/openapi/model/competitionEndAuditLogEntry.ts b/frontend/openapi/model/competitionEndAuditLogEntry.ts deleted file mode 100644 index 241f39b0e..000000000 --- a/frontend/openapi/model/competitionEndAuditLogEntry.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { CompetitionEndAuditLogEntryAllOf } from './competitionEndAuditLogEntryAllOf'; -import { AuditLogEntry } from './auditLogEntry'; - - -export interface CompetitionEndAuditLogEntry { - api: CompetitionEndAuditLogEntry.ApiEnum; - user?: string; - competition_1rSl5jE: string; -} -export namespace CompetitionEndAuditLogEntry { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/competitionEndAuditLogEntryAllOf.ts b/frontend/openapi/model/competitionEndAuditLogEntryAllOf.ts deleted file mode 100644 index 9427cd430..000000000 --- a/frontend/openapi/model/competitionEndAuditLogEntryAllOf.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface CompetitionEndAuditLogEntryAllOf { - api?: CompetitionEndAuditLogEntryAllOf.ApiEnum; - user?: string; - competition_1rSl5jE?: string; -} -export namespace CompetitionEndAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/competitionOverview.ts b/frontend/openapi/model/competitionOverview.ts deleted file mode 100644 index f6548d936..000000000 --- a/frontend/openapi/model/competitionOverview.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface CompetitionOverview { - id: string; - name: string; - description: string; - taskCount: number; - teamCount: number; -} - diff --git a/frontend/openapi/model/competitionStartAuditLogEntry.ts b/frontend/openapi/model/competitionStartAuditLogEntry.ts deleted file mode 100644 index 4ead4d56e..000000000 --- a/frontend/openapi/model/competitionStartAuditLogEntry.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { CompetitionEndAuditLogEntryAllOf } from './competitionEndAuditLogEntryAllOf'; -import { AuditLogEntry } from './auditLogEntry'; - - -export interface CompetitionStartAuditLogEntry { - type: CompetitionStartAuditLogEntry.TypeEnum; - timestamp: number; - timestamp$backend?: number; - id_1rSl5jE: string; - id_Fwp7XY8?: string; - api: CompetitionStartAuditLogEntry.ApiEnum; - user?: string; - competition_1rSl5jE: string; -} -export namespace CompetitionStartAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITIONSTART: 'COMPETITION_START' as TypeEnum, - COMPETITIONEND: 'COMPETITION_END' as TypeEnum, - TASKSTART: 'TASK_START' as TypeEnum, - TASKMODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASKEND: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/competitionStartMessage.ts b/frontend/openapi/model/competitionStartMessage.ts deleted file mode 100644 index fd54694bd..000000000 --- a/frontend/openapi/model/competitionStartMessage.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface CompetitionStartMessage { - competitionId: string; - name: string; - type: CompetitionStartMessage.TypeEnum; - scoreboards: Array; -} -export namespace CompetitionStartMessage { - export type TypeEnum = 'SYNCHRONOUS' | 'ASYNCHRONOUS'; - export const TypeEnum = { - SYNCHRONOUS: 'SYNCHRONOUS' as TypeEnum, - ASYNCHRONOUS: 'ASYNCHRONOUS' as TypeEnum - }; -} - - diff --git a/frontend/openapi/model/configuredOptionOptions.ts b/frontend/openapi/model/configuredOptionOptions.ts deleted file mode 100644 index d897c90b5..000000000 --- a/frontend/openapi/model/configuredOptionOptions.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ConfiguredOptionOptions { - option: ConfiguredOptionOptions.OptionEnum; - parameters: { [key: string]: string; }; -} -export namespace ConfiguredOptionOptions { - export type OptionEnum = 'HIDDEN_RESULTS' | 'MAP_TO_SEGMENT'; - export const OptionEnum = { - HIDDEN_RESULTS: 'HIDDEN_RESULTS' as OptionEnum, - MAP_TO_SEGMENT: 'MAP_TO_SEGMENT' as OptionEnum - }; -} - - diff --git a/frontend/openapi/model/configuredOptionQueryComponentType.ts b/frontend/openapi/model/configuredOptionQueryComponentType.ts deleted file mode 100644 index 1375c4ca7..000000000 --- a/frontend/openapi/model/configuredOptionQueryComponentType.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ConfiguredOptionQueryComponentType { - option: ConfiguredOptionQueryComponentType.OptionEnum; - parameters: { [key: string]: string; }; -} -export namespace ConfiguredOptionQueryComponentType { - export type OptionEnum = 'IMAGE_ITEM' | 'VIDEO_ITEM_SEGMENT' | 'TEXT' | 'EXTERNAL_IMAGE' | 'EXTERNAL_VIDEO'; - export const OptionEnum = { - IMAGE_ITEM: 'IMAGE_ITEM' as OptionEnum, - VIDEO_ITEM_SEGMENT: 'VIDEO_ITEM_SEGMENT' as OptionEnum, - TEXT: 'TEXT' as OptionEnum, - EXTERNAL_IMAGE: 'EXTERNAL_IMAGE' as OptionEnum, - EXTERNAL_VIDEO: 'EXTERNAL_VIDEO' as OptionEnum - }; -} - - diff --git a/frontend/openapi/model/configuredOptionScoringType.ts b/frontend/openapi/model/configuredOptionScoringType.ts deleted file mode 100644 index 09495507d..000000000 --- a/frontend/openapi/model/configuredOptionScoringType.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ConfiguredOptionScoringType { - option: ConfiguredOptionScoringType.OptionEnum; - parameters: { [key: string]: string; }; -} -export namespace ConfiguredOptionScoringType { - export type OptionEnum = 'KIS' | 'AVS'; - export const OptionEnum = { - KIS: 'KIS' as OptionEnum, - AVS: 'AVS' as OptionEnum - }; -} - - diff --git a/frontend/openapi/model/configuredOptionSubmissionFilterType.ts b/frontend/openapi/model/configuredOptionSubmissionFilterType.ts deleted file mode 100644 index 0028bb7fa..000000000 --- a/frontend/openapi/model/configuredOptionSubmissionFilterType.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ConfiguredOptionSubmissionFilterType { - option: ConfiguredOptionSubmissionFilterType.OptionEnum; - parameters: { [key: string]: string; }; -} -export namespace ConfiguredOptionSubmissionFilterType { - export type OptionEnum = 'NO_DUPLICATES' | 'LIMIT_CORRECT_PER_TEAM' | 'LIMIT_WRONG_PER_TEAM' | 'LIMIT_TOTAL_PER_TEAM' | 'LIMIT_CORRECT_PER_MEMBER' | 'TEMPORAL_SUBMISSION'; - export const OptionEnum = { - NO_DUPLICATES: 'NO_DUPLICATES' as OptionEnum, - LIMIT_CORRECT_PER_TEAM: 'LIMIT_CORRECT_PER_TEAM' as OptionEnum, - LIMIT_WRONG_PER_TEAM: 'LIMIT_WRONG_PER_TEAM' as OptionEnum, - LIMIT_TOTAL_PER_TEAM: 'LIMIT_TOTAL_PER_TEAM' as OptionEnum, - LIMIT_CORRECT_PER_MEMBER: 'LIMIT_CORRECT_PER_MEMBER' as OptionEnum, - TEMPORAL_SUBMISSION: 'TEMPORAL_SUBMISSION' as OptionEnum - }; -} - - diff --git a/frontend/openapi/model/configuredOptionTargetType.ts b/frontend/openapi/model/configuredOptionTargetType.ts deleted file mode 100644 index acbf9925d..000000000 --- a/frontend/openapi/model/configuredOptionTargetType.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ConfiguredOptionTargetType { - option: ConfiguredOptionTargetType.OptionEnum; - parameters: { [key: string]: string; }; -} -export namespace ConfiguredOptionTargetType { - export type OptionEnum = 'SINGLE_MEDIA_ITEM' | 'SINGLE_MEDIA_SEGMENT' | 'MULTIPLE_MEDIA_ITEMS' | 'JUDGEMENT' | 'VOTE'; - export const OptionEnum = { - SINGLE_MEDIA_ITEM: 'SINGLE_MEDIA_ITEM' as OptionEnum, - SINGLE_MEDIA_SEGMENT: 'SINGLE_MEDIA_SEGMENT' as OptionEnum, - MULTIPLE_MEDIA_ITEMS: 'MULTIPLE_MEDIA_ITEMS' as OptionEnum, - JUDGEMENT: 'JUDGEMENT' as OptionEnum, - VOTE: 'VOTE' as OptionEnum - }; -} - - diff --git a/frontend/openapi/model/contentElement.ts b/frontend/openapi/model/contentElement.ts deleted file mode 100644 index e43966e69..000000000 --- a/frontend/openapi/model/contentElement.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ContentElement { - contentType: ContentElement.ContentTypeEnum; - content?: string; - offset: number; -} -export namespace ContentElement { - export type ContentTypeEnum = 'EMPTY' | 'TEXT' | 'VIDEO' | 'IMAGE'; - export const ContentTypeEnum = { - EMPTY: 'EMPTY' as ContentTypeEnum, - TEXT: 'TEXT' as ContentTypeEnum, - VIDEO: 'VIDEO' as ContentTypeEnum, - IMAGE: 'IMAGE' as ContentTypeEnum - }; -} - - diff --git a/frontend/openapi/model/currentTime.ts b/frontend/openapi/model/currentTime.ts deleted file mode 100644 index cdc683998..000000000 --- a/frontend/openapi/model/currentTime.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface CurrentTime { - timeStamp: number; -} - diff --git a/frontend/openapi/model/errorStatus.ts b/frontend/openapi/model/errorStatus.ts deleted file mode 100644 index 37f0b7fc5..000000000 --- a/frontend/openapi/model/errorStatus.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ErrorStatus { - description: string; - status: boolean; -} - diff --git a/frontend/openapi/model/externalImageTaskDescriptionHint.ts b/frontend/openapi/model/externalImageTaskDescriptionHint.ts deleted file mode 100644 index fcc2b97fe..000000000 --- a/frontend/openapi/model/externalImageTaskDescriptionHint.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ExternalImageTaskDescriptionHintAllOfImageLocation } from './externalImageTaskDescriptionHintAllOfImageLocation'; -import { TaskDescriptionHint } from './taskDescriptionHint'; -import { ExternalImageTaskDescriptionHintAllOf } from './externalImageTaskDescriptionHintAllOf'; - - -export interface ExternalImageTaskDescriptionHint { - imageLocation: ExternalImageTaskDescriptionHintAllOfImageLocation; -} -export namespace ExternalImageTaskDescriptionHint { -} - - diff --git a/frontend/openapi/model/externalImageTaskDescriptionHintAllOf.ts b/frontend/openapi/model/externalImageTaskDescriptionHintAllOf.ts deleted file mode 100644 index 5aea19f53..000000000 --- a/frontend/openapi/model/externalImageTaskDescriptionHintAllOf.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ExternalImageTaskDescriptionHintAllOfImageLocation } from './externalImageTaskDescriptionHintAllOfImageLocation'; - - -export interface ExternalImageTaskDescriptionHintAllOf { - imageLocation?: ExternalImageTaskDescriptionHintAllOfImageLocation; -} - diff --git a/frontend/openapi/model/externalImageTaskDescriptionHintAllOfImageLocation.ts b/frontend/openapi/model/externalImageTaskDescriptionHintAllOfImageLocation.ts deleted file mode 100644 index fc1a39aef..000000000 --- a/frontend/openapi/model/externalImageTaskDescriptionHintAllOfImageLocation.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ExternalImageTaskDescriptionHintAllOfImageLocationFileSystem } from './externalImageTaskDescriptionHintAllOfImageLocationFileSystem'; - - -export interface ExternalImageTaskDescriptionHintAllOfImageLocation { - nameCount?: number; - absolute?: boolean; - fileSystem?: ExternalImageTaskDescriptionHintAllOfImageLocationFileSystem; -} - diff --git a/frontend/openapi/model/externalImageTaskDescriptionHintAllOfImageLocationFileSystem.ts b/frontend/openapi/model/externalImageTaskDescriptionHintAllOfImageLocationFileSystem.ts deleted file mode 100644 index f5ab39fcf..000000000 --- a/frontend/openapi/model/externalImageTaskDescriptionHintAllOfImageLocationFileSystem.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ExternalImageTaskDescriptionHintAllOfImageLocationFileSystem { - rootDirectories?: object; - fileStores?: object; - userPrincipalLookupService?: object; - open?: boolean; - readOnly?: boolean; - separator?: string; -} - diff --git a/frontend/openapi/model/externalVideoTaskDescriptionHint.ts b/frontend/openapi/model/externalVideoTaskDescriptionHint.ts deleted file mode 100644 index e5ea830b0..000000000 --- a/frontend/openapi/model/externalVideoTaskDescriptionHint.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ExternalVideoTaskDescriptionHintAllOf } from './externalVideoTaskDescriptionHintAllOf'; -import { ExternalImageTaskDescriptionHintAllOfImageLocation } from './externalImageTaskDescriptionHintAllOfImageLocation'; -import { TaskDescriptionHint } from './taskDescriptionHint'; - - -export interface ExternalVideoTaskDescriptionHint { - videoLocation: ExternalImageTaskDescriptionHintAllOfImageLocation; -} -export namespace ExternalVideoTaskDescriptionHint { -} - - diff --git a/frontend/openapi/model/externalVideoTaskDescriptionHintAllOf.ts b/frontend/openapi/model/externalVideoTaskDescriptionHintAllOf.ts deleted file mode 100644 index 1c1ff7bd2..000000000 --- a/frontend/openapi/model/externalVideoTaskDescriptionHintAllOf.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ExternalImageTaskDescriptionHintAllOfImageLocation } from './externalImageTaskDescriptionHintAllOfImageLocation'; - - -export interface ExternalVideoTaskDescriptionHintAllOf { - videoLocation?: ExternalImageTaskDescriptionHintAllOfImageLocation; -} - diff --git a/frontend/openapi/model/imageItem.ts b/frontend/openapi/model/imageItem.ts deleted file mode 100644 index a7a5dbf9e..000000000 --- a/frontend/openapi/model/imageItem.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ImageItem { - name: string; - location: string; - id_1rSl5jE: string; - id_Fwp7XY8?: string; - collection_1rSl5jE: string; -} - diff --git a/frontend/openapi/model/imageItemTaskDescriptionHint.ts b/frontend/openapi/model/imageItemTaskDescriptionHint.ts deleted file mode 100644 index 5f1fde5c5..000000000 --- a/frontend/openapi/model/imageItemTaskDescriptionHint.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ImageItem } from './imageItem'; -import { TaskDescriptionHint } from './taskDescriptionHint'; -import { ImageItemTaskDescriptionHintAllOf } from './imageItemTaskDescriptionHintAllOf'; - - -export interface ImageItemTaskDescriptionHint { - item: ImageItem; -} -export namespace ImageItemTaskDescriptionHint { -} - - diff --git a/frontend/openapi/model/imageItemTaskDescriptionHintAllOf.ts b/frontend/openapi/model/imageItemTaskDescriptionHintAllOf.ts deleted file mode 100644 index 1cf6e7eb2..000000000 --- a/frontend/openapi/model/imageItemTaskDescriptionHintAllOf.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ImageItem } from './imageItem'; - - -export interface ImageItemTaskDescriptionHintAllOf { - item?: ImageItem; -} - diff --git a/frontend/openapi/model/itemSubmission.ts b/frontend/openapi/model/itemSubmission.ts deleted file mode 100644 index 874adbec3..000000000 --- a/frontend/openapi/model/itemSubmission.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskRun } from './taskRun'; -import { Submission } from './submission'; -import { MediaItem } from './mediaItem'; - - -export interface ItemSubmission extends Submission { -} -export namespace ItemSubmission { -} - - diff --git a/frontend/openapi/model/judgement.ts b/frontend/openapi/model/judgement.ts deleted file mode 100644 index 094e20574..000000000 --- a/frontend/openapi/model/judgement.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface Judgement { - token: string; - validator: string; - verdict: Judgement.VerdictEnum; -} -export namespace Judgement { - export type VerdictEnum = 'CORRECT' | 'WRONG' | 'INDETERMINATE' | 'UNDECIDABLE'; - export const VerdictEnum = { - CORRECT: 'CORRECT' as VerdictEnum, - WRONG: 'WRONG' as VerdictEnum, - INDETERMINATE: 'INDETERMINATE' as VerdictEnum, - UNDECIDABLE: 'UNDECIDABLE' as VerdictEnum - }; -} - - diff --git a/frontend/openapi/model/judgementAuditLogEntry.ts b/frontend/openapi/model/judgementAuditLogEntry.ts deleted file mode 100644 index 947d10b28..000000000 --- a/frontend/openapi/model/judgementAuditLogEntry.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { AuditLogEntry } from './auditLogEntry'; -import { JudgementAuditLogEntryAllOf } from './judgementAuditLogEntryAllOf'; - - -export interface JudgementAuditLogEntry { - validator: string; - token: string; - verdict: JudgementAuditLogEntry.VerdictEnum; - api: JudgementAuditLogEntry.ApiEnum; - user?: string; - competition_1rSl5jE: string; -} -export namespace JudgementAuditLogEntry { - export type VerdictEnum = 'CORRECT' | 'WRONG' | 'INDETERMINATE' | 'UNDECIDABLE'; - export const VerdictEnum = { - CORRECT: 'CORRECT' as VerdictEnum, - WRONG: 'WRONG' as VerdictEnum, - INDETERMINATE: 'INDETERMINATE' as VerdictEnum, - UNDECIDABLE: 'UNDECIDABLE' as VerdictEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/judgementAuditLogEntryAllOf.ts b/frontend/openapi/model/judgementAuditLogEntryAllOf.ts deleted file mode 100644 index 9092870dd..000000000 --- a/frontend/openapi/model/judgementAuditLogEntryAllOf.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface JudgementAuditLogEntryAllOf { - validator?: string; - token?: string; - verdict?: JudgementAuditLogEntryAllOf.VerdictEnum; - api?: JudgementAuditLogEntryAllOf.ApiEnum; - user?: string; - competition_1rSl5jE?: string; -} -export namespace JudgementAuditLogEntryAllOf { - export type VerdictEnum = 'CORRECT' | 'WRONG' | 'INDETERMINATE' | 'UNDECIDABLE'; - export const VerdictEnum = { - CORRECT: 'CORRECT' as VerdictEnum, - WRONG: 'WRONG' as VerdictEnum, - INDETERMINATE: 'INDETERMINATE' as VerdictEnum, - UNDECIDABLE: 'UNDECIDABLE' as VerdictEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/judgementRequest.ts b/frontend/openapi/model/judgementRequest.ts deleted file mode 100644 index 9f5168edd..000000000 --- a/frontend/openapi/model/judgementRequest.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface JudgementRequest { - token: string; - validator: string; - collection: string; - item: string; - taskDescription: string; - startTime?: string; - endTime?: string; -} - diff --git a/frontend/openapi/model/judgementTaskDescriptionTarget.ts b/frontend/openapi/model/judgementTaskDescriptionTarget.ts deleted file mode 100644 index 4201e1c33..000000000 --- a/frontend/openapi/model/judgementTaskDescriptionTarget.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskDescriptionTarget } from './taskDescriptionTarget'; - - -export interface JudgementTaskDescriptionTarget extends TaskDescriptionTarget { -} - diff --git a/frontend/openapi/model/judgementValidatorStatus.ts b/frontend/openapi/model/judgementValidatorStatus.ts deleted file mode 100644 index 2adff1ea1..000000000 --- a/frontend/openapi/model/judgementValidatorStatus.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface JudgementValidatorStatus { - validator: string; - pending: number; - open: number; -} - diff --git a/frontend/openapi/model/judgementVote.ts b/frontend/openapi/model/judgementVote.ts deleted file mode 100644 index cf3051cfd..000000000 --- a/frontend/openapi/model/judgementVote.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface JudgementVote { - verdict: JudgementVote.VerdictEnum; -} -export namespace JudgementVote { - export type VerdictEnum = 'CORRECT' | 'WRONG' | 'INDETERMINATE' | 'UNDECIDABLE'; - export const VerdictEnum = { - CORRECT: 'CORRECT' as VerdictEnum, - WRONG: 'WRONG' as VerdictEnum, - INDETERMINATE: 'INDETERMINATE' as VerdictEnum, - UNDECIDABLE: 'UNDECIDABLE' as VerdictEnum - }; -} - - diff --git a/frontend/openapi/model/loginAuditLogEntry.ts b/frontend/openapi/model/loginAuditLogEntry.ts deleted file mode 100644 index 164c30743..000000000 --- a/frontend/openapi/model/loginAuditLogEntry.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { AuditLogEntry } from './auditLogEntry'; -import { LoginAuditLogEntryAllOf } from './loginAuditLogEntryAllOf'; - - -export interface LoginAuditLogEntry { - user: string; - session: string; - api: LoginAuditLogEntry.ApiEnum; -} -export namespace LoginAuditLogEntry { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/loginAuditLogEntryAllOf.ts b/frontend/openapi/model/loginAuditLogEntryAllOf.ts deleted file mode 100644 index 285da8693..000000000 --- a/frontend/openapi/model/loginAuditLogEntryAllOf.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface LoginAuditLogEntryAllOf { - user?: string; - session?: string; - api?: LoginAuditLogEntryAllOf.ApiEnum; -} -export namespace LoginAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/loginRequest.ts b/frontend/openapi/model/loginRequest.ts deleted file mode 100644 index fe78fe6e2..000000000 --- a/frontend/openapi/model/loginRequest.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface LoginRequest { - username: string; - password: string; -} - diff --git a/frontend/openapi/model/logoutAuditLogEntry.ts b/frontend/openapi/model/logoutAuditLogEntry.ts deleted file mode 100644 index 3cc2cb617..000000000 --- a/frontend/openapi/model/logoutAuditLogEntry.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { AuditLogEntry } from './auditLogEntry'; -import { LogoutAuditLogEntryAllOf } from './logoutAuditLogEntryAllOf'; - - -export interface LogoutAuditLogEntry { - session: string; - api: LogoutAuditLogEntry.ApiEnum; -} -export namespace LogoutAuditLogEntry { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/logoutAuditLogEntryAllOf.ts b/frontend/openapi/model/logoutAuditLogEntryAllOf.ts deleted file mode 100644 index c948a2c88..000000000 --- a/frontend/openapi/model/logoutAuditLogEntryAllOf.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface LogoutAuditLogEntryAllOf { - session?: string; - api?: LogoutAuditLogEntryAllOf.ApiEnum; -} -export namespace LogoutAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/mediaItem.ts b/frontend/openapi/model/mediaItem.ts deleted file mode 100644 index a16ad5598..000000000 --- a/frontend/openapi/model/mediaItem.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface MediaItem { - name: string; - location: string; - collection_1rSl5jE: string; - id_1rSl5jE: string; - id_Fwp7XY8?: string; -} - diff --git a/frontend/openapi/model/mediaItemTarget.ts b/frontend/openapi/model/mediaItemTarget.ts deleted file mode 100644 index 3575570be..000000000 --- a/frontend/openapi/model/mediaItemTarget.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { MediaItem } from './mediaItem'; -import { MediaItemTargetAllOf } from './mediaItemTargetAllOf'; -import { TaskDescriptionTarget } from './taskDescriptionTarget'; - - -export interface MediaItemTarget { - item: MediaItem; -} - diff --git a/frontend/openapi/model/mediaItemTargetAllOf.ts b/frontend/openapi/model/mediaItemTargetAllOf.ts deleted file mode 100644 index 0f2722bc0..000000000 --- a/frontend/openapi/model/mediaItemTargetAllOf.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { MediaItem } from './mediaItem'; - - -export interface MediaItemTargetAllOf { - item?: MediaItem; -} - diff --git a/frontend/openapi/model/models.ts b/frontend/openapi/model/models.ts deleted file mode 100644 index ef6eea13f..000000000 --- a/frontend/openapi/model/models.ts +++ /dev/null @@ -1,73 +0,0 @@ -export * from './auditLogInfo'; -export * from './clientRunInfo'; -export * from './clientRunInfoList'; -export * from './clientTaskInfo'; -export * from './competitionCreate'; -export * from './competitionOverview'; -export * from './competitionStartMessage'; -export * from './configuredOptionOptions'; -export * from './configuredOptionQueryComponentType'; -export * from './configuredOptionScoringType'; -export * from './configuredOptionSubmissionFilterType'; -export * from './configuredOptionTargetType'; -export * from './contentElement'; -export * from './currentTime'; -export * from './errorStatus'; -export * from './judgement'; -export * from './judgementRequest'; -export * from './judgementValidatorStatus'; -export * from './judgementVote'; -export * from './loginRequest'; -export * from './queryEvent'; -export * from './queryEventLog'; -export * from './queryResult'; -export * from './queryResultLog'; -export * from './restAuditLogEntry'; -export * from './restCompetitionDescription'; -export * from './restCompetitionEndAuditLogEntry'; -export * from './restCompetitionEndAuditLogEntryAllOf'; -export * from './restCompetitionStartAuditLogEntry'; -export * from './restDetailedTeam'; -export * from './restFullMediaCollection'; -export * from './restJudgementAuditLogEntry'; -export * from './restJudgementAuditLogEntryAllOf'; -export * from './restLoginAuditLogEntry'; -export * from './restLoginAuditLogEntryAllOf'; -export * from './restLogoutAuditLogEntry'; -export * from './restLogoutAuditLogEntryAllOf'; -export * from './restMediaCollection'; -export * from './restMediaItem'; -export * from './restPrepareJudgementAuditLogEntry'; -export * from './restPrepareJudgementAuditLogEntryAllOf'; -export * from './restSubmissionAuditLogEntry'; -export * from './restSubmissionAuditLogEntryAllOf'; -export * from './restTaskDescription'; -export * from './restTaskDescriptionComponent'; -export * from './restTaskDescriptionTarget'; -export * from './restTaskDescriptionTargetItem'; -export * from './restTaskEndAuditLogEntry'; -export * from './restTaskEndAuditLogEntryAllOf'; -export * from './restTaskModifiedAuditLogEntry'; -export * from './restTaskModifiedAuditLogEntryAllOf'; -export * from './restTaskStartAuditLogEntry'; -export * from './restTeam'; -export * from './runInfo'; -export * from './runState'; -export * from './score'; -export * from './scoreOverview'; -export * from './scoreSeries'; -export * from './scoreSeriesPoint'; -export * from './sessionId'; -export * from './submissionInfo'; -export * from './successStatus'; -export * from './taskGroup'; -export * from './taskHint'; -export * from './taskInfo'; -export * from './taskTarget'; -export * from './taskType'; -export * from './teamInfo'; -export * from './temporalPoint'; -export * from './temporalRange'; -export * from './userDetails'; -export * from './userRequest'; -export * from './viewerInfo'; diff --git a/frontend/openapi/model/multipleMediaItemTarget.ts b/frontend/openapi/model/multipleMediaItemTarget.ts deleted file mode 100644 index 81e2655b9..000000000 --- a/frontend/openapi/model/multipleMediaItemTarget.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { MultipleMediaItemTargetAllOf } from './multipleMediaItemTargetAllOf'; -import { MediaItem } from './mediaItem'; -import { TaskDescriptionTarget } from './taskDescriptionTarget'; - - -export interface MultipleMediaItemTarget { - items: Array; -} - diff --git a/frontend/openapi/model/multipleMediaItemTargetAllOf.ts b/frontend/openapi/model/multipleMediaItemTargetAllOf.ts deleted file mode 100644 index 9e710d8d5..000000000 --- a/frontend/openapi/model/multipleMediaItemTargetAllOf.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { MediaItem } from './mediaItem'; - - -export interface MultipleMediaItemTargetAllOf { - items?: Array; -} - diff --git a/frontend/openapi/model/queryEvent.ts b/frontend/openapi/model/queryEvent.ts deleted file mode 100644 index 5f56b2a7e..000000000 --- a/frontend/openapi/model/queryEvent.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface QueryEvent { - timestamp: number; - category: QueryEvent.CategoryEnum; - type: string; - value: string; -} -export namespace QueryEvent { - export type CategoryEnum = 'TEXT' | 'IMAGE' | 'SKETCH' | 'FILTER' | 'BROWSING' | 'COOPERATION' | 'OTHER'; - export const CategoryEnum = { - TEXT: 'TEXT' as CategoryEnum, - IMAGE: 'IMAGE' as CategoryEnum, - SKETCH: 'SKETCH' as CategoryEnum, - FILTER: 'FILTER' as CategoryEnum, - BROWSING: 'BROWSING' as CategoryEnum, - COOPERATION: 'COOPERATION' as CategoryEnum, - OTHER: 'OTHER' as CategoryEnum - }; -} - - diff --git a/frontend/openapi/model/queryEventLog.ts b/frontend/openapi/model/queryEventLog.ts deleted file mode 100644 index 4757cd514..000000000 --- a/frontend/openapi/model/queryEventLog.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { QueryEvent } from './queryEvent'; - - -export interface QueryEventLog { - timestamp: number; - events: Array; -} - diff --git a/frontend/openapi/model/queryResult.ts b/frontend/openapi/model/queryResult.ts deleted file mode 100644 index 5fe8d749d..000000000 --- a/frontend/openapi/model/queryResult.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface QueryResult { - item: string; - segment?: number; - frame?: number; - score?: number; - rank?: number; -} - diff --git a/frontend/openapi/model/queryResultLog.ts b/frontend/openapi/model/queryResultLog.ts deleted file mode 100644 index 66269f991..000000000 --- a/frontend/openapi/model/queryResultLog.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { QueryResult } from './queryResult'; -import { QueryEvent } from './queryEvent'; - - -export interface QueryResultLog { - timestamp: number; - sortType: string; - resultSetAvailability: string; - results: Array; - events: Array; -} - diff --git a/frontend/openapi/model/restAuditLogEntry.ts b/frontend/openapi/model/restAuditLogEntry.ts deleted file mode 100644 index 09bb27c98..000000000 --- a/frontend/openapi/model/restAuditLogEntry.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestAuditLogEntry { - type: RestAuditLogEntry.TypeEnum; - id: string; - timestamp: number; -} -export namespace RestAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; -} - - diff --git a/frontend/openapi/model/restCompetitionDescription.ts b/frontend/openapi/model/restCompetitionDescription.ts deleted file mode 100644 index f5ad3d41c..000000000 --- a/frontend/openapi/model/restCompetitionDescription.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskType } from './taskType'; -import { RestTeam } from './restTeam'; -import { TaskGroup } from './taskGroup'; -import { RestTaskDescription } from './restTaskDescription'; - - -export interface RestCompetitionDescription { - id: string; - name: string; - description?: string; - taskTypes: Array; - taskGroups: Array; - tasks: Array; - teams: Array; - participantCanView: boolean; -} - diff --git a/frontend/openapi/model/restCompetitionEndAuditLogEntry.ts b/frontend/openapi/model/restCompetitionEndAuditLogEntry.ts deleted file mode 100644 index 428b695a0..000000000 --- a/frontend/openapi/model/restCompetitionEndAuditLogEntry.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestAuditLogEntry } from './restAuditLogEntry'; -import { RestCompetitionEndAuditLogEntryAllOf } from './restCompetitionEndAuditLogEntryAllOf'; - - -export interface RestCompetitionEndAuditLogEntry { - type: RestCompetitionEndAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - competition: string; - api: RestCompetitionEndAuditLogEntry.ApiEnum; - user?: string; -} -export namespace RestCompetitionEndAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restCompetitionEndAuditLogEntryAllOf.ts b/frontend/openapi/model/restCompetitionEndAuditLogEntryAllOf.ts deleted file mode 100644 index a9d60b481..000000000 --- a/frontend/openapi/model/restCompetitionEndAuditLogEntryAllOf.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestCompetitionEndAuditLogEntryAllOf { - competition?: string; - api?: RestCompetitionEndAuditLogEntryAllOf.ApiEnum; - user?: string; -} -export namespace RestCompetitionEndAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restCompetitionStartAuditLogEntry.ts b/frontend/openapi/model/restCompetitionStartAuditLogEntry.ts deleted file mode 100644 index daf17a831..000000000 --- a/frontend/openapi/model/restCompetitionStartAuditLogEntry.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestAuditLogEntry } from './restAuditLogEntry'; -import { RestCompetitionEndAuditLogEntryAllOf } from './restCompetitionEndAuditLogEntryAllOf'; - - -export interface RestCompetitionStartAuditLogEntry { - type: RestCompetitionStartAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - competition: string; - api: RestCompetitionStartAuditLogEntry.ApiEnum; - user?: string; -} -export namespace RestCompetitionStartAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restDetailedTeam.ts b/frontend/openapi/model/restDetailedTeam.ts deleted file mode 100644 index bc53d789d..000000000 --- a/frontend/openapi/model/restDetailedTeam.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { UserDetails } from './userDetails'; - - -export interface RestDetailedTeam { - name: string; - color: string; - logoId: string; - users: Array; -} - diff --git a/frontend/openapi/model/restFullMediaCollection.ts b/frontend/openapi/model/restFullMediaCollection.ts deleted file mode 100644 index ae130eec4..000000000 --- a/frontend/openapi/model/restFullMediaCollection.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestMediaItem } from './restMediaItem'; -import { RestMediaCollection } from './restMediaCollection'; - - -export interface RestFullMediaCollection { - collection: RestMediaCollection; - items: Array; -} - diff --git a/frontend/openapi/model/restJudgementAuditLogEntry.ts b/frontend/openapi/model/restJudgementAuditLogEntry.ts deleted file mode 100644 index b5c5112a5..000000000 --- a/frontend/openapi/model/restJudgementAuditLogEntry.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestAuditLogEntry } from './restAuditLogEntry'; -import { RestJudgementAuditLogEntryAllOf } from './restJudgementAuditLogEntryAllOf'; - - -export interface RestJudgementAuditLogEntry { - type: RestJudgementAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - competition: string; - validator: string; - token: string; - verdict: RestJudgementAuditLogEntry.VerdictEnum; - api: RestJudgementAuditLogEntry.ApiEnum; - user?: string; -} -export namespace RestJudgementAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type VerdictEnum = 'CORRECT' | 'WRONG' | 'INDETERMINATE' | 'UNDECIDABLE'; - export const VerdictEnum = { - CORRECT: 'CORRECT' as VerdictEnum, - WRONG: 'WRONG' as VerdictEnum, - INDETERMINATE: 'INDETERMINATE' as VerdictEnum, - UNDECIDABLE: 'UNDECIDABLE' as VerdictEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restJudgementAuditLogEntryAllOf.ts b/frontend/openapi/model/restJudgementAuditLogEntryAllOf.ts deleted file mode 100644 index 8c1a629ec..000000000 --- a/frontend/openapi/model/restJudgementAuditLogEntryAllOf.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestJudgementAuditLogEntryAllOf { - competition?: string; - validator?: string; - token?: string; - verdict?: RestJudgementAuditLogEntryAllOf.VerdictEnum; - api?: RestJudgementAuditLogEntryAllOf.ApiEnum; - user?: string; -} -export namespace RestJudgementAuditLogEntryAllOf { - export type VerdictEnum = 'CORRECT' | 'WRONG' | 'INDETERMINATE' | 'UNDECIDABLE'; - export const VerdictEnum = { - CORRECT: 'CORRECT' as VerdictEnum, - WRONG: 'WRONG' as VerdictEnum, - INDETERMINATE: 'INDETERMINATE' as VerdictEnum, - UNDECIDABLE: 'UNDECIDABLE' as VerdictEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restLoginAuditLogEntry.ts b/frontend/openapi/model/restLoginAuditLogEntry.ts deleted file mode 100644 index a61996842..000000000 --- a/frontend/openapi/model/restLoginAuditLogEntry.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestAuditLogEntry } from './restAuditLogEntry'; -import { RestLoginAuditLogEntryAllOf } from './restLoginAuditLogEntryAllOf'; - - -export interface RestLoginAuditLogEntry { - type: RestLoginAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - user: string; - session: string; - api: RestLoginAuditLogEntry.ApiEnum; -} -export namespace RestLoginAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restLoginAuditLogEntryAllOf.ts b/frontend/openapi/model/restLoginAuditLogEntryAllOf.ts deleted file mode 100644 index 457c1f6cb..000000000 --- a/frontend/openapi/model/restLoginAuditLogEntryAllOf.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestLoginAuditLogEntryAllOf { - user?: string; - session?: string; - api?: RestLoginAuditLogEntryAllOf.ApiEnum; -} -export namespace RestLoginAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restLogoutAuditLogEntry.ts b/frontend/openapi/model/restLogoutAuditLogEntry.ts deleted file mode 100644 index 656b40fd7..000000000 --- a/frontend/openapi/model/restLogoutAuditLogEntry.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestLogoutAuditLogEntryAllOf } from './restLogoutAuditLogEntryAllOf'; -import { RestAuditLogEntry } from './restAuditLogEntry'; - - -export interface RestLogoutAuditLogEntry { - type: RestLogoutAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - session: string; - api: RestLogoutAuditLogEntry.ApiEnum; -} -export namespace RestLogoutAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restLogoutAuditLogEntryAllOf.ts b/frontend/openapi/model/restLogoutAuditLogEntryAllOf.ts deleted file mode 100644 index 968d5c82a..000000000 --- a/frontend/openapi/model/restLogoutAuditLogEntryAllOf.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestLogoutAuditLogEntryAllOf { - session?: string; - api?: RestLogoutAuditLogEntryAllOf.ApiEnum; -} -export namespace RestLogoutAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restMediaCollection.ts b/frontend/openapi/model/restMediaCollection.ts deleted file mode 100644 index 9f110ce9c..000000000 --- a/frontend/openapi/model/restMediaCollection.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestMediaCollection { - id: string; - name: string; - description?: string; - basePath?: string; -} - diff --git a/frontend/openapi/model/restMediaItem.ts b/frontend/openapi/model/restMediaItem.ts deleted file mode 100644 index 867b6b5b9..000000000 --- a/frontend/openapi/model/restMediaItem.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestMediaItem { - id: string; - name: string; - type: RestMediaItem.TypeEnum; - collectionId: string; - location: string; - durationMs?: number; - fps?: number; -} -export namespace RestMediaItem { - export type TypeEnum = 'IMAGE' | 'VIDEO'; - export const TypeEnum = { - IMAGE: 'IMAGE' as TypeEnum, - VIDEO: 'VIDEO' as TypeEnum - }; -} - - diff --git a/frontend/openapi/model/restPrepareJudgementAuditLogEntry.ts b/frontend/openapi/model/restPrepareJudgementAuditLogEntry.ts deleted file mode 100644 index dc7866aaa..000000000 --- a/frontend/openapi/model/restPrepareJudgementAuditLogEntry.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { SubmissionInfo } from './submissionInfo'; -import { RestPrepareJudgementAuditLogEntryAllOf } from './restPrepareJudgementAuditLogEntryAllOf'; -import { RestAuditLogEntry } from './restAuditLogEntry'; - - -export interface RestPrepareJudgementAuditLogEntry { - type: RestPrepareJudgementAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - validator: string; - token: string; - submission: SubmissionInfo; -} -export namespace RestPrepareJudgementAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; -} - - diff --git a/frontend/openapi/model/restPrepareJudgementAuditLogEntryAllOf.ts b/frontend/openapi/model/restPrepareJudgementAuditLogEntryAllOf.ts deleted file mode 100644 index cc2dc76ca..000000000 --- a/frontend/openapi/model/restPrepareJudgementAuditLogEntryAllOf.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { SubmissionInfo } from './submissionInfo'; - - -export interface RestPrepareJudgementAuditLogEntryAllOf { - validator?: string; - token?: string; - submission?: SubmissionInfo; -} - diff --git a/frontend/openapi/model/restSubmissionAuditLogEntry.ts b/frontend/openapi/model/restSubmissionAuditLogEntry.ts deleted file mode 100644 index 6b38a01df..000000000 --- a/frontend/openapi/model/restSubmissionAuditLogEntry.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { SubmissionInfo } from './submissionInfo'; -import { RestSubmissionAuditLogEntryAllOf } from './restSubmissionAuditLogEntryAllOf'; -import { RestAuditLogEntry } from './restAuditLogEntry'; - - -export interface RestSubmissionAuditLogEntry { - type: RestSubmissionAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - competition: string; - taskName: string; - submission: SubmissionInfo; - api: RestSubmissionAuditLogEntry.ApiEnum; - user?: string; - address: string; -} -export namespace RestSubmissionAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restSubmissionAuditLogEntryAllOf.ts b/frontend/openapi/model/restSubmissionAuditLogEntryAllOf.ts deleted file mode 100644 index c22872591..000000000 --- a/frontend/openapi/model/restSubmissionAuditLogEntryAllOf.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { SubmissionInfo } from './submissionInfo'; - - -export interface RestSubmissionAuditLogEntryAllOf { - competition?: string; - taskName?: string; - submission?: SubmissionInfo; - api?: RestSubmissionAuditLogEntryAllOf.ApiEnum; - user?: string; - address?: string; -} -export namespace RestSubmissionAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restTaskDescription.ts b/frontend/openapi/model/restTaskDescription.ts deleted file mode 100644 index 46ae33874..000000000 --- a/frontend/openapi/model/restTaskDescription.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestTaskDescriptionComponent } from './restTaskDescriptionComponent'; -import { RestTaskDescriptionTarget } from './restTaskDescriptionTarget'; - - -export interface RestTaskDescription { - id: string; - name: string; - taskGroup: string; - taskType: string; - duration: number; - mediaCollectionId: string; - target: RestTaskDescriptionTarget; - components: Array; -} - diff --git a/frontend/openapi/model/restTaskDescriptionComponent.ts b/frontend/openapi/model/restTaskDescriptionComponent.ts deleted file mode 100644 index 4614ca9b9..000000000 --- a/frontend/openapi/model/restTaskDescriptionComponent.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TemporalRange } from './temporalRange'; - - -export interface RestTaskDescriptionComponent { - type: RestTaskDescriptionComponent.TypeEnum; - start?: number; - end?: number; - description?: string; - path?: string; - dataType?: string; - mediaItem?: string; - range?: TemporalRange; -} -export namespace RestTaskDescriptionComponent { - export type TypeEnum = 'IMAGE_ITEM' | 'VIDEO_ITEM_SEGMENT' | 'TEXT' | 'EXTERNAL_IMAGE' | 'EXTERNAL_VIDEO'; - export const TypeEnum = { - IMAGE_ITEM: 'IMAGE_ITEM' as TypeEnum, - VIDEO_ITEM_SEGMENT: 'VIDEO_ITEM_SEGMENT' as TypeEnum, - TEXT: 'TEXT' as TypeEnum, - EXTERNAL_IMAGE: 'EXTERNAL_IMAGE' as TypeEnum, - EXTERNAL_VIDEO: 'EXTERNAL_VIDEO' as TypeEnum - }; -} - - diff --git a/frontend/openapi/model/restTaskDescriptionTarget.ts b/frontend/openapi/model/restTaskDescriptionTarget.ts deleted file mode 100644 index d7513d9c1..000000000 --- a/frontend/openapi/model/restTaskDescriptionTarget.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestTaskDescriptionTargetItem } from './restTaskDescriptionTargetItem'; - - -export interface RestTaskDescriptionTarget { - type: RestTaskDescriptionTarget.TypeEnum; - mediaItems: Array; -} -export namespace RestTaskDescriptionTarget { - export type TypeEnum = 'SINGLE_MEDIA_ITEM' | 'SINGLE_MEDIA_SEGMENT' | 'MULTIPLE_MEDIA_ITEMS' | 'JUDGEMENT' | 'VOTE'; - export const TypeEnum = { - SINGLE_MEDIA_ITEM: 'SINGLE_MEDIA_ITEM' as TypeEnum, - SINGLE_MEDIA_SEGMENT: 'SINGLE_MEDIA_SEGMENT' as TypeEnum, - MULTIPLE_MEDIA_ITEMS: 'MULTIPLE_MEDIA_ITEMS' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - VOTE: 'VOTE' as TypeEnum - }; -} - - diff --git a/frontend/openapi/model/restTaskDescriptionTargetItem.ts b/frontend/openapi/model/restTaskDescriptionTargetItem.ts deleted file mode 100644 index 9e372eba5..000000000 --- a/frontend/openapi/model/restTaskDescriptionTargetItem.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TemporalRange } from './temporalRange'; - - -export interface RestTaskDescriptionTargetItem { - mediaItem: string; - temporalRange?: TemporalRange; -} - diff --git a/frontend/openapi/model/restTaskEndAuditLogEntry.ts b/frontend/openapi/model/restTaskEndAuditLogEntry.ts deleted file mode 100644 index c990080ba..000000000 --- a/frontend/openapi/model/restTaskEndAuditLogEntry.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestAuditLogEntry } from './restAuditLogEntry'; -import { RestTaskEndAuditLogEntryAllOf } from './restTaskEndAuditLogEntryAllOf'; - - -export interface RestTaskEndAuditLogEntry { - type: RestTaskEndAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - competition: string; - taskName: string; - api: RestTaskEndAuditLogEntry.ApiEnum; - user?: string; -} -export namespace RestTaskEndAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restTaskEndAuditLogEntryAllOf.ts b/frontend/openapi/model/restTaskEndAuditLogEntryAllOf.ts deleted file mode 100644 index 5337e4152..000000000 --- a/frontend/openapi/model/restTaskEndAuditLogEntryAllOf.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestTaskEndAuditLogEntryAllOf { - competition?: string; - taskName?: string; - api?: RestTaskEndAuditLogEntryAllOf.ApiEnum; - user?: string; -} -export namespace RestTaskEndAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restTaskModifiedAuditLogEntry.ts b/frontend/openapi/model/restTaskModifiedAuditLogEntry.ts deleted file mode 100644 index d5e271e99..000000000 --- a/frontend/openapi/model/restTaskModifiedAuditLogEntry.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestTaskModifiedAuditLogEntryAllOf } from './restTaskModifiedAuditLogEntryAllOf'; -import { RestAuditLogEntry } from './restAuditLogEntry'; - - -export interface RestTaskModifiedAuditLogEntry { - type: RestTaskModifiedAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - competition: string; - taskName: string; - modification: string; - api: RestTaskModifiedAuditLogEntry.ApiEnum; - user?: string; -} -export namespace RestTaskModifiedAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restTaskModifiedAuditLogEntryAllOf.ts b/frontend/openapi/model/restTaskModifiedAuditLogEntryAllOf.ts deleted file mode 100644 index 0f5087704..000000000 --- a/frontend/openapi/model/restTaskModifiedAuditLogEntryAllOf.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestTaskModifiedAuditLogEntryAllOf { - competition?: string; - taskName?: string; - modification?: string; - api?: RestTaskModifiedAuditLogEntryAllOf.ApiEnum; - user?: string; -} -export namespace RestTaskModifiedAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restTaskStartAuditLogEntry.ts b/frontend/openapi/model/restTaskStartAuditLogEntry.ts deleted file mode 100644 index 904c7230e..000000000 --- a/frontend/openapi/model/restTaskStartAuditLogEntry.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestAuditLogEntry } from './restAuditLogEntry'; -import { RestTaskEndAuditLogEntryAllOf } from './restTaskEndAuditLogEntryAllOf'; - - -export interface RestTaskStartAuditLogEntry { - type: RestTaskStartAuditLogEntry.TypeEnum; - id: string; - timestamp: number; - competition: string; - taskName: string; - api: RestTaskStartAuditLogEntry.ApiEnum; - user?: string; -} -export namespace RestTaskStartAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'PREPARE_JUDGEMENT' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITION_START: 'COMPETITION_START' as TypeEnum, - COMPETITION_END: 'COMPETITION_END' as TypeEnum, - TASK_START: 'TASK_START' as TypeEnum, - TASK_MODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASK_END: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - PREPARE_JUDGEMENT: 'PREPARE_JUDGEMENT' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/restTeam.ts b/frontend/openapi/model/restTeam.ts deleted file mode 100644 index 0a85f086c..000000000 --- a/frontend/openapi/model/restTeam.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface RestTeam { - uid?: string; - name: string; - color: string; - logoData?: string; - logoId?: string; - users: Array; -} - diff --git a/frontend/openapi/model/runInfo.ts b/frontend/openapi/model/runInfo.ts deleted file mode 100644 index 8d02ce9a3..000000000 --- a/frontend/openapi/model/runInfo.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskInfo } from './taskInfo'; -import { TeamInfo } from './teamInfo'; - - -export interface RunInfo { - id: string; - name: string; - description?: string; - teams: Array; - tasks: Array; - competitionId: string; - participantsCanView: boolean; -} - diff --git a/frontend/openapi/model/runScoreSeries.ts b/frontend/openapi/model/runScoreSeries.ts deleted file mode 100644 index 8a3dd7ff2..000000000 --- a/frontend/openapi/model/runScoreSeries.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ScoreSeries } from './scoreSeries'; - - -export interface RunScoreSeries { - teams: Array; - names: Array; - series: Array; -} - diff --git a/frontend/openapi/model/runState.ts b/frontend/openapi/model/runState.ts deleted file mode 100644 index a2751e9ab..000000000 --- a/frontend/openapi/model/runState.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskInfo } from './taskInfo'; - - -export interface RunState { - id: string; - status: RunState.StatusEnum; - currentTask?: TaskInfo; - timeLeft: number; -} -export namespace RunState { - export type StatusEnum = 'CREATED' | 'ACTIVE' | 'PREPARING_TASK' | 'RUNNING_TASK' | 'TASK_ENDED' | 'TERMINATED'; - export const StatusEnum = { - CREATED: 'CREATED' as StatusEnum, - ACTIVE: 'ACTIVE' as StatusEnum, - PREPARING_TASK: 'PREPARING_TASK' as StatusEnum, - RUNNING_TASK: 'RUNNING_TASK' as StatusEnum, - TASK_ENDED: 'TASK_ENDED' as StatusEnum, - TERMINATED: 'TERMINATED' as StatusEnum - }; -} - - diff --git a/frontend/openapi/model/score.ts b/frontend/openapi/model/score.ts deleted file mode 100644 index 917fc5eab..000000000 --- a/frontend/openapi/model/score.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface Score { - teamId: string; - score: number; -} - diff --git a/frontend/openapi/model/scoreOverview.ts b/frontend/openapi/model/scoreOverview.ts deleted file mode 100644 index 88ea3db30..000000000 --- a/frontend/openapi/model/scoreOverview.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { Score } from './score'; - - -export interface ScoreOverview { - name: string; - taskGroup?: string; - scores: Array; -} - diff --git a/frontend/openapi/model/scoreSeries.ts b/frontend/openapi/model/scoreSeries.ts deleted file mode 100644 index 3862a87c3..000000000 --- a/frontend/openapi/model/scoreSeries.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ScoreSeriesPoint } from './scoreSeriesPoint'; - - -export interface ScoreSeries { - team: string; - name: string; - points: Array; -} - diff --git a/frontend/openapi/model/scoreSeriesPoint.ts b/frontend/openapi/model/scoreSeriesPoint.ts deleted file mode 100644 index a1b111259..000000000 --- a/frontend/openapi/model/scoreSeriesPoint.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ScoreSeriesPoint { - score: number; - timestamp: number; -} - diff --git a/frontend/openapi/model/sessionId.ts b/frontend/openapi/model/sessionId.ts deleted file mode 100644 index fc76758fa..000000000 --- a/frontend/openapi/model/sessionId.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface SessionId { - sessionId: string; -} - diff --git a/frontend/openapi/model/submission.ts b/frontend/openapi/model/submission.ts deleted file mode 100644 index 52f91f254..000000000 --- a/frontend/openapi/model/submission.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskRun } from './taskRun'; -import { MediaItem } from './mediaItem'; - - -export interface Submission { - teamId?: string; - memberId?: string; - timestamp: number; - item: MediaItem; - uid?: string; - status: Submission.StatusEnum; - uid_1rSl5jE: string; - teamId_1rSl5jE: string; - memberId_1rSl5jE: string; - taskRun$backend?: TaskRun; -} -export namespace Submission { - export type StatusEnum = 'CORRECT' | 'WRONG' | 'INDETERMINATE' | 'UNDECIDABLE'; - export const StatusEnum = { - CORRECT: 'CORRECT' as StatusEnum, - WRONG: 'WRONG' as StatusEnum, - INDETERMINATE: 'INDETERMINATE' as StatusEnum, - UNDECIDABLE: 'UNDECIDABLE' as StatusEnum - }; -} - - diff --git a/frontend/openapi/model/submissionAuditLogEntry.ts b/frontend/openapi/model/submissionAuditLogEntry.ts deleted file mode 100644 index 0d2654677..000000000 --- a/frontend/openapi/model/submissionAuditLogEntry.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { SubmissionAuditLogEntryAllOf } from './submissionAuditLogEntryAllOf'; -import { AuditLogEntry } from './auditLogEntry'; -import { Submission } from './submission'; - - -export interface SubmissionAuditLogEntry { - taskName: string; - submission: Submission; - api: SubmissionAuditLogEntry.ApiEnum; - user?: string; - address: string; - competition_1rSl5jE: string; -} -export namespace SubmissionAuditLogEntry { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/submissionAuditLogEntryAllOf.ts b/frontend/openapi/model/submissionAuditLogEntryAllOf.ts deleted file mode 100644 index 58b9e3eb9..000000000 --- a/frontend/openapi/model/submissionAuditLogEntryAllOf.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { Submission } from './submission'; - - -export interface SubmissionAuditLogEntryAllOf { - taskName?: string; - submission?: Submission; - api?: SubmissionAuditLogEntryAllOf.ApiEnum; - user?: string; - address?: string; - competition_1rSl5jE?: string; -} -export namespace SubmissionAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/submissionInfo.ts b/frontend/openapi/model/submissionInfo.ts deleted file mode 100644 index 384fb10a6..000000000 --- a/frontend/openapi/model/submissionInfo.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { RestMediaItem } from './restMediaItem'; - - -export interface SubmissionInfo { - id?: string; - team: string; - member: string; - status: SubmissionInfo.StatusEnum; - timestamp: number; - item?: RestMediaItem; - start?: number; - end?: number; -} -export namespace SubmissionInfo { - export type StatusEnum = 'CORRECT' | 'WRONG' | 'INDETERMINATE' | 'UNDECIDABLE'; - export const StatusEnum = { - CORRECT: 'CORRECT' as StatusEnum, - WRONG: 'WRONG' as StatusEnum, - INDETERMINATE: 'INDETERMINATE' as StatusEnum, - UNDECIDABLE: 'UNDECIDABLE' as StatusEnum - }; -} - - diff --git a/frontend/openapi/model/successStatus.ts b/frontend/openapi/model/successStatus.ts deleted file mode 100644 index f29e7a3e3..000000000 --- a/frontend/openapi/model/successStatus.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface SuccessStatus { - description: string; - status: boolean; -} - diff --git a/frontend/openapi/model/taskDescription.ts b/frontend/openapi/model/taskDescription.ts deleted file mode 100644 index c96d5a6a7..000000000 --- a/frontend/openapi/model/taskDescription.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskType } from './taskType'; -import { TaskDescriptionHint } from './taskDescriptionHint'; -import { TaskGroup } from './taskGroup'; -import { TaskDescriptionTarget } from './taskDescriptionTarget'; - - -export interface TaskDescription { - name: string; - taskGroup: TaskGroup; - taskType: TaskType; - duration: number; - target: TaskDescriptionTarget; - hints: Array; - mediaCollectionId_1rSl5jE: string; - id_1rSl5jE: string; -} - diff --git a/frontend/openapi/model/taskDescriptionHint.ts b/frontend/openapi/model/taskDescriptionHint.ts deleted file mode 100644 index 3f91aeb5f..000000000 --- a/frontend/openapi/model/taskDescriptionHint.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TaskDescriptionHint { - contentType?: TaskDescriptionHint.ContentTypeEnum; - ordinal: number; - end?: number; - contentType$backend: TaskDescriptionHint.ContentTypebackendEnum; - start?: number; -} -export namespace TaskDescriptionHint { - export type ContentTypeEnum = 'TEXT' | 'VIDEO' | 'IMAGE'; - export const ContentTypeEnum = { - TEXT: 'TEXT' as ContentTypeEnum, - VIDEO: 'VIDEO' as ContentTypeEnum, - IMAGE: 'IMAGE' as ContentTypeEnum - }; - export type ContentTypebackendEnum = 'TEXT' | 'VIDEO' | 'IMAGE'; - export const ContentTypebackendEnum = { - TEXT: 'TEXT' as ContentTypebackendEnum, - VIDEO: 'VIDEO' as ContentTypebackendEnum, - IMAGE: 'IMAGE' as ContentTypebackendEnum - }; -} - - diff --git a/frontend/openapi/model/taskDescriptionTarget.ts b/frontend/openapi/model/taskDescriptionTarget.ts deleted file mode 100644 index 6de6dc0a3..000000000 --- a/frontend/openapi/model/taskDescriptionTarget.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TaskDescriptionTarget { - ordinal: number; -} - diff --git a/frontend/openapi/model/taskEndAuditLogEntry.ts b/frontend/openapi/model/taskEndAuditLogEntry.ts deleted file mode 100644 index 8cfb091cd..000000000 --- a/frontend/openapi/model/taskEndAuditLogEntry.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { AuditLogEntry } from './auditLogEntry'; -import { TaskEndAuditLogEntryAllOf } from './taskEndAuditLogEntryAllOf'; - - -export interface TaskEndAuditLogEntry { - taskName: string; - api: TaskEndAuditLogEntry.ApiEnum; - user?: string; - competition_1rSl5jE: string; -} -export namespace TaskEndAuditLogEntry { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/taskEndAuditLogEntryAllOf.ts b/frontend/openapi/model/taskEndAuditLogEntryAllOf.ts deleted file mode 100644 index 2157ae6b3..000000000 --- a/frontend/openapi/model/taskEndAuditLogEntryAllOf.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TaskEndAuditLogEntryAllOf { - taskName?: string; - api?: TaskEndAuditLogEntryAllOf.ApiEnum; - user?: string; - competition_1rSl5jE?: string; -} -export namespace TaskEndAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/taskGroup.ts b/frontend/openapi/model/taskGroup.ts deleted file mode 100644 index 6b4e304b5..000000000 --- a/frontend/openapi/model/taskGroup.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TaskGroup { - name: string; - type: string; -} - diff --git a/frontend/openapi/model/taskHint.ts b/frontend/openapi/model/taskHint.ts deleted file mode 100644 index 4df3d8f78..000000000 --- a/frontend/openapi/model/taskHint.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ContentElement } from './contentElement'; - - -export interface TaskHint { - taskId: string; - sequence: Array; - loop: boolean; -} - diff --git a/frontend/openapi/model/taskInfo.ts b/frontend/openapi/model/taskInfo.ts deleted file mode 100644 index 076435db5..000000000 --- a/frontend/openapi/model/taskInfo.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TaskInfo { - id: string; - name: string; - taskGroup: string; - taskType: string; - duration: number; -} - diff --git a/frontend/openapi/model/taskModifiedAuditLogEntry.ts b/frontend/openapi/model/taskModifiedAuditLogEntry.ts deleted file mode 100644 index e075cd7af..000000000 --- a/frontend/openapi/model/taskModifiedAuditLogEntry.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskModifiedAuditLogEntryAllOf } from './taskModifiedAuditLogEntryAllOf'; -import { AuditLogEntry } from './auditLogEntry'; - - -export interface TaskModifiedAuditLogEntry { - taskName: string; - modification: string; - api: TaskModifiedAuditLogEntry.ApiEnum; - user?: string; - competition_1rSl5jE: string; -} -export namespace TaskModifiedAuditLogEntry { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/taskModifiedAuditLogEntryAllOf.ts b/frontend/openapi/model/taskModifiedAuditLogEntryAllOf.ts deleted file mode 100644 index fece8d537..000000000 --- a/frontend/openapi/model/taskModifiedAuditLogEntryAllOf.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TaskModifiedAuditLogEntryAllOf { - taskName?: string; - modification?: string; - api?: TaskModifiedAuditLogEntryAllOf.ApiEnum; - user?: string; - competition_1rSl5jE?: string; -} -export namespace TaskModifiedAuditLogEntryAllOf { - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/taskRun.ts b/frontend/openapi/model/taskRun.ts deleted file mode 100644 index fccb68d3b..000000000 --- a/frontend/openapi/model/taskRun.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { Submission } from './submission'; -import { TaskDescription } from './taskDescription'; - - -export interface TaskRun { - submissions: Array; - task: TaskDescription; - filter: object; - scorer: object; - validator: object; - started?: number; - ended?: number; - duration: number; - uid_1rSl5jE: string; - taskId_1rSl5jE: string; - hasStarted?: boolean; - hasEnded?: boolean; - running?: boolean; - position: number; -} - diff --git a/frontend/openapi/model/taskStartAuditLogEntry.ts b/frontend/openapi/model/taskStartAuditLogEntry.ts deleted file mode 100644 index 75a772b72..000000000 --- a/frontend/openapi/model/taskStartAuditLogEntry.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { AuditLogEntry } from './auditLogEntry'; -import { TaskEndAuditLogEntryAllOf } from './taskEndAuditLogEntryAllOf'; - - -export interface TaskStartAuditLogEntry { - type: TaskStartAuditLogEntry.TypeEnum; - timestamp: number; - timestamp$backend?: number; - id_1rSl5jE: string; - id_Fwp7XY8?: string; - taskName: string; - api: TaskStartAuditLogEntry.ApiEnum; - user?: string; - competition_1rSl5jE: string; -} -export namespace TaskStartAuditLogEntry { - export type TypeEnum = 'COMPETITION_START' | 'COMPETITION_END' | 'TASK_START' | 'TASK_MODIFIED' | 'TASK_END' | 'SUBMISSION' | 'JUDGEMENT' | 'LOGIN' | 'LOGOUT'; - export const TypeEnum = { - COMPETITIONSTART: 'COMPETITION_START' as TypeEnum, - COMPETITIONEND: 'COMPETITION_END' as TypeEnum, - TASKSTART: 'TASK_START' as TypeEnum, - TASKMODIFIED: 'TASK_MODIFIED' as TypeEnum, - TASKEND: 'TASK_END' as TypeEnum, - SUBMISSION: 'SUBMISSION' as TypeEnum, - JUDGEMENT: 'JUDGEMENT' as TypeEnum, - LOGIN: 'LOGIN' as TypeEnum, - LOGOUT: 'LOGOUT' as TypeEnum - }; - export type ApiEnum = 'REST' | 'CLI' | 'INTERNAL'; - export const ApiEnum = { - REST: 'REST' as ApiEnum, - CLI: 'CLI' as ApiEnum, - INTERNAL: 'INTERNAL' as ApiEnum - }; -} - - diff --git a/frontend/openapi/model/taskTarget.ts b/frontend/openapi/model/taskTarget.ts deleted file mode 100644 index a3b24ebf5..000000000 --- a/frontend/openapi/model/taskTarget.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ContentElement } from './contentElement'; - - -export interface TaskTarget { - taskId: string; - sequence: Array; -} - diff --git a/frontend/openapi/model/taskType.ts b/frontend/openapi/model/taskType.ts deleted file mode 100644 index b7af3036f..000000000 --- a/frontend/openapi/model/taskType.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ConfiguredOptionOptions } from './configuredOptionOptions'; -import { ConfiguredOptionQueryComponentType } from './configuredOptionQueryComponentType'; -import { ConfiguredOptionScoringType } from './configuredOptionScoringType'; -import { ConfiguredOptionSubmissionFilterType } from './configuredOptionSubmissionFilterType'; -import { ConfiguredOptionTargetType } from './configuredOptionTargetType'; - - -export interface TaskType { - name: string; - taskDuration: number; - targetType: ConfiguredOptionTargetType; - components: Array; - score: ConfiguredOptionScoringType; - filter: Array; - options: Array; -} - diff --git a/frontend/openapi/model/team.ts b/frontend/openapi/model/team.ts deleted file mode 100644 index c46241dd3..000000000 --- a/frontend/openapi/model/team.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { UID } from './uID'; - - -export interface Team { - name: string; - color: string; - users: Array; - uid_1rSl5jE: string; - logoId_1rSl5jE: string; -} - diff --git a/frontend/openapi/model/teamInfo.ts b/frontend/openapi/model/teamInfo.ts deleted file mode 100644 index 525389806..000000000 --- a/frontend/openapi/model/teamInfo.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TeamInfo { - uid: string; - name: string; - color: string; - logoId: string; -} - diff --git a/frontend/openapi/model/temporalPoint.ts b/frontend/openapi/model/temporalPoint.ts deleted file mode 100644 index 0d0d162ed..000000000 --- a/frontend/openapi/model/temporalPoint.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TemporalPoint { - value: number; - unit: TemporalPoint.UnitEnum; -} -export namespace TemporalPoint { - export type UnitEnum = 'FRAME_NUMBER' | 'SECONDS' | 'MILLISECONDS'; - export const UnitEnum = { - FRAME_NUMBER: 'FRAME_NUMBER' as UnitEnum, - SECONDS: 'SECONDS' as UnitEnum, - MILLISECONDS: 'MILLISECONDS' as UnitEnum - }; -} - - diff --git a/frontend/openapi/model/temporalRange.ts b/frontend/openapi/model/temporalRange.ts deleted file mode 100644 index 7e2469a51..000000000 --- a/frontend/openapi/model/temporalRange.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TemporalPoint } from './temporalPoint'; - - -export interface TemporalRange { - start: TemporalPoint; - end: TemporalPoint; -} - diff --git a/frontend/openapi/model/temporalSubmission.ts b/frontend/openapi/model/temporalSubmission.ts deleted file mode 100644 index ae7cc54b6..000000000 --- a/frontend/openapi/model/temporalSubmission.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TaskRun } from './taskRun'; -import { Submission } from './submission'; -import { MediaItem } from './mediaItem'; -import { TemporalRange } from './temporalRange'; -import { TemporalSubmissionAllOf } from './temporalSubmissionAllOf'; - - -export interface TemporalSubmission { - start: number; - end: number; - temporalRange: TemporalRange; -} -export namespace TemporalSubmission { -} - - diff --git a/frontend/openapi/model/temporalSubmissionAllOf.ts b/frontend/openapi/model/temporalSubmissionAllOf.ts deleted file mode 100644 index 990d3a693..000000000 --- a/frontend/openapi/model/temporalSubmissionAllOf.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TemporalRange } from './temporalRange'; - - -export interface TemporalSubmissionAllOf { - start?: number; - end?: number; - temporalRange?: TemporalRange; -} - diff --git a/frontend/openapi/model/textTaskDescriptionHint.ts b/frontend/openapi/model/textTaskDescriptionHint.ts deleted file mode 100644 index c04f94941..000000000 --- a/frontend/openapi/model/textTaskDescriptionHint.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TextTaskDescriptionHintAllOf } from './textTaskDescriptionHintAllOf'; -import { TaskDescriptionHint } from './taskDescriptionHint'; - - -export interface TextTaskDescriptionHint { - text: string; -} -export namespace TextTaskDescriptionHint { -} - - diff --git a/frontend/openapi/model/textTaskDescriptionHintAllOf.ts b/frontend/openapi/model/textTaskDescriptionHintAllOf.ts deleted file mode 100644 index c5d935800..000000000 --- a/frontend/openapi/model/textTaskDescriptionHintAllOf.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface TextTaskDescriptionHintAllOf { - text?: string; -} - diff --git a/frontend/openapi/model/uID.ts b/frontend/openapi/model/uID.ts deleted file mode 100644 index e2ac37f77..000000000 --- a/frontend/openapi/model/uID.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface UID { - string: string; -} - diff --git a/frontend/openapi/model/userDetails.ts b/frontend/openapi/model/userDetails.ts deleted file mode 100644 index a5a0d9b7e..000000000 --- a/frontend/openapi/model/userDetails.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface UserDetails { - id: string; - username: string; - role: UserDetails.RoleEnum; - sessionId?: string; -} -export namespace UserDetails { - export type RoleEnum = 'ADMIN' | 'JUDGE' | 'VIEWER' | 'PARTICIPANT'; - export const RoleEnum = { - ADMIN: 'ADMIN' as RoleEnum, - JUDGE: 'JUDGE' as RoleEnum, - VIEWER: 'VIEWER' as RoleEnum, - PARTICIPANT: 'PARTICIPANT' as RoleEnum - }; -} - - diff --git a/frontend/openapi/model/userRequest.ts b/frontend/openapi/model/userRequest.ts deleted file mode 100644 index 55b011274..000000000 --- a/frontend/openapi/model/userRequest.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface UserRequest { - username: string; - password?: string; - role?: UserRequest.RoleEnum; -} -export namespace UserRequest { - export type RoleEnum = 'ADMIN' | 'JUDGE' | 'VIEWER' | 'PARTICIPANT'; - export const RoleEnum = { - ADMIN: 'ADMIN' as RoleEnum, - JUDGE: 'JUDGE' as RoleEnum, - VIEWER: 'VIEWER' as RoleEnum, - PARTICIPANT: 'PARTICIPANT' as RoleEnum - }; -} - - diff --git a/frontend/openapi/model/videoItem.ts b/frontend/openapi/model/videoItem.ts deleted file mode 100644 index 2d2d80100..000000000 --- a/frontend/openapi/model/videoItem.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { VideoItemAllOf } from './videoItemAllOf'; -import { MediaItem } from './mediaItem'; - - -export interface VideoItem { - durationMs: number; - fps: number; -} - diff --git a/frontend/openapi/model/videoItemAllOf.ts b/frontend/openapi/model/videoItemAllOf.ts deleted file mode 100644 index 8bf94284b..000000000 --- a/frontend/openapi/model/videoItemAllOf.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface VideoItemAllOf { - durationMs?: number; - fps?: number; -} - diff --git a/frontend/openapi/model/videoItemSegmentTaskDescriptionHint.ts b/frontend/openapi/model/videoItemSegmentTaskDescriptionHint.ts deleted file mode 100644 index 98447540c..000000000 --- a/frontend/openapi/model/videoItemSegmentTaskDescriptionHint.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TemporalRange } from './temporalRange'; -import { TaskDescriptionHint } from './taskDescriptionHint'; -import { VideoItem } from './videoItem'; -import { VideoItemSegmentTaskDescriptionHintAllOf } from './videoItemSegmentTaskDescriptionHintAllOf'; - - -export interface VideoItemSegmentTaskDescriptionHint { - item: VideoItem; - temporalRange: TemporalRange; -} -export namespace VideoItemSegmentTaskDescriptionHint { -} - - diff --git a/frontend/openapi/model/videoItemSegmentTaskDescriptionHintAllOf.ts b/frontend/openapi/model/videoItemSegmentTaskDescriptionHintAllOf.ts deleted file mode 100644 index e9c1e7bc8..000000000 --- a/frontend/openapi/model/videoItemSegmentTaskDescriptionHintAllOf.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TemporalRange } from './temporalRange'; -import { VideoItem } from './videoItem'; - - -export interface VideoItemSegmentTaskDescriptionHintAllOf { - item?: VideoItem; - temporalRange?: TemporalRange; -} - diff --git a/frontend/openapi/model/videoSegmentTarget.ts b/frontend/openapi/model/videoSegmentTarget.ts deleted file mode 100644 index d3d20af95..000000000 --- a/frontend/openapi/model/videoSegmentTarget.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { TemporalRange } from './temporalRange'; -import { VideoItem } from './videoItem'; -import { VideoItemSegmentTaskDescriptionHintAllOf } from './videoItemSegmentTaskDescriptionHintAllOf'; -import { TaskDescriptionTarget } from './taskDescriptionTarget'; - - -export interface VideoSegmentTarget { - ordinal: number; - item: VideoItem; - temporalRange: TemporalRange; -} - diff --git a/frontend/openapi/model/viewerInfo.ts b/frontend/openapi/model/viewerInfo.ts deleted file mode 100644 index 675587611..000000000 --- a/frontend/openapi/model/viewerInfo.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * DRES API - * API for DRES (Distributed Retrieval Evaluation Server), Version 1.0 - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -export interface ViewerInfo { - viewersId: string; - username: string; - host: string; - ready: boolean; -} - diff --git a/frontend/openapi/ng-package.json b/frontend/openapi/ng-package.json deleted file mode 100644 index 3b17900dc..000000000 --- a/frontend/openapi/ng-package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "./node_modules/ng-packagr/ng-package.schema.json", - "lib": { - "entryFile": "index.ts" - } -} diff --git a/frontend/openapi/package.json b/frontend/openapi/package.json deleted file mode 100644 index 4d216cdff..000000000 --- a/frontend/openapi/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@dres-openapi/api", - "version": "1.0-SNAPSHOT.202103031232", - "description": "OpenAPI client for @dres-openapi/api", - "author": "OpenAPI-Generator Contributors", - "keywords": [ - "openapi-client", - "openapi-generator" - ], - "license": "Unlicense", - "scripts": { - "build": "ng-packagr -p ng-package.json" - }, - "peerDependencies": { - "@angular/core": "^9.1.0", - "rxjs": "^6.5.3" - }, - "devDependencies": { - "@angular/common": "^9.1.0", - "@angular/compiler": "^9.1.0", - "@angular/compiler-cli": "^9.1.0", - "@angular/core": "^9.1.0", - "@angular/platform-browser": "^9.1.0", - "ng-packagr": "^9.0.1", - "reflect-metadata": "^0.1.3", - "rxjs": "^6.5.3", - "tsickle": "^0.38.0", - "typescript": ">=3.6.0 <3.8.0", - "zone.js": "^0.10.2" - }} diff --git a/frontend/openapi/tsconfig.json b/frontend/openapi/tsconfig.json deleted file mode 100644 index c01ebe255..000000000 --- a/frontend/openapi/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "noImplicitAny": false, - "suppressImplicitAnyIndexErrors": true, - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "removeComments": true, - "sourceMap": true, - "outDir": "./dist", - "noLib": false, - "declaration": true, - "lib": [ "es6", "dom" ], - "typeRoots": [ - "node_modules/@types" - ] - }, - "exclude": [ - "node_modules", - "dist" - ], - "filesGlob": [ - "./model/*.ts", - "./api/*.ts" - ] -} diff --git a/frontend/openapi/variables.ts b/frontend/openapi/variables.ts deleted file mode 100644 index 6fe58549f..000000000 --- a/frontend/openapi/variables.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { InjectionToken } from '@angular/core'; - -export const BASE_PATH = new InjectionToken('basePath'); -export const COLLECTION_FORMATS = { - 'csv': ',', - 'tsv': ' ', - 'ssv': ' ', - 'pipes': '|' -} From 1a120670bb0a0d0efe4c9961f3f82e7e1ba637fc Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 3 Mar 2021 13:42:31 +0100 Subject: [PATCH 37/95] Regenerated oas --- doc/oas-client.json | 2 +- doc/oas.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/oas-client.json b/doc/oas-client.json index 211d84194..488abd8a8 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index 2b03e3aaa..746c5d5b3 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionOptions":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionQueryComponentType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetType"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentType"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringType"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterType"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionOptions"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionOptions":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionQueryComponentType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetType"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentType"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringType"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterType"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionOptions"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file From d3a0cbce4d973815df9946a7b9cf0848b415fbf3 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 3 Mar 2021 16:52:31 +0100 Subject: [PATCH 38/95] removed 'required' flag from session parameter for logout and status calls --- .../dres/api/rest/handler/LogoutHandler.kt | 2 +- .../dev/dres/api/rest/handler/UserHandler.kt | 2 +- doc/oas-client.json | 2 +- doc/oas.json | 2 +- doc/openapi-v3-dres-client.json | 993 ---- doc/openapi-v3.json | 3981 ----------------- 6 files changed, 4 insertions(+), 4978 deletions(-) delete mode 100644 doc/openapi-v3-dres-client.json delete mode 100644 doc/openapi-v3.json diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt index 3bcbdf68d..acab899f5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt @@ -19,7 +19,7 @@ class LogoutHandler(private val audit: DAO) : RestHandler, GetRes @OpenApi(summary = "Clears all user roles of the current session.", path = "/api/logout", tags = ["User"], queryParams = [ - OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + OpenApiParam("session", String::class, "Session Token") ], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt index 312b88277..46336c2bb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt @@ -225,7 +225,7 @@ class CurrentUsersSessionIdHandler : UserHandler(), GetRestHandler, A path = "/api/user/session", tags = ["User"], queryParams = [ - OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + OpenApiParam("session", String::class, "Session Token") ], responses = [ OpenApiResponse("200", [OpenApiContent(SessionId::class)]), diff --git a/doc/oas-client.json b/doc/oas-client.json index 488abd8a8..3ec126810 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index 746c5d5b3..0f4c5bb1a 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionOptions":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionQueryComponentType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetType"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentType"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringType"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterType"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionOptions"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionOptions":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionQueryComponentType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetType"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentType"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringType"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterType"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionOptions"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file diff --git a/doc/openapi-v3-dres-client.json b/doc/openapi-v3-dres-client.json deleted file mode 100644 index df4ef4568..000000000 --- a/doc/openapi-v3-dres-client.json +++ /dev/null @@ -1,993 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "DRES API", - "description": "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", - "version": "1.0" - }, - "paths": { - "/api/login": { - "post": { - "tags": [ - "User" - ], - "summary": "Sets roles for session based on user account and returns a session cookie.", - "operationId": "postApiLogin", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LoginRequest" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/logout": { - "get": { - "tags": [ - "User" - ], - "summary": "Clears all user roles of the current session.", - "operationId": "getApiLogout", - "parameters": [ - { - "name": "session", - "in": "query", - "description": "Session Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/user/{userId}": { - "get": { - "tags": [ - "User" - ], - "summary": "Gets details of the user with the given id", - "operationId": "getApiUserWithUserid", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User's UID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "404": { - "description": "If the user could not be found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "delete": { - "tags": [ - "User" - ], - "summary": "Deletes the specified user. Requires ADMIN privileges", - "operationId": "deleteApiUserWithUserid", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User ID", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "404": { - "description": "If the user could not be found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "patch": { - "tags": [ - "User" - ], - "summary": "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", - "operationId": "patchApiUserWithUserid", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User ID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserRequest" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/user/session": { - "get": { - "tags": [ - "User" - ], - "summary": "Get current sessionId", - "operationId": "getApiUserSession", - "parameters": [ - { - "name": "session", - "in": "query", - "description": "Session Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionId" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/status/time": { - "get": { - "tags": [ - "Status" - ], - "summary": "Returns the current time on the server.", - "operationId": "getApiStatusTime", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CurrentTime" - } - } - } - } - } - } - }, - "/api/runInfo/list": { - "get": { - "tags": [ - "Client Run Info" - ], - "summary": "Lists an overview of all competition runs visible to the current client", - "operationId": "getApiRuninfoList", - "parameters": [ - { - "name": "session", - "in": "query", - "description": "Session Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ClientRunInfoList" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/runInfo/currentTask/{runId}": { - "get": { - "tags": [ - "Client Run Info" - ], - "summary": "Returns an overview of the currently active task for a run", - "operationId": "getApiRuninfoCurrenttaskWithRunid", - "parameters": [ - { - "name": "runId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "session", - "in": "query", - "description": "Session Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ClientTaskInfo" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/submit": { - "get": { - "tags": [ - "Submission" - ], - "summary": "Endpoint to accept submissions", - "operationId": "getSubmit", - "parameters": [ - { - "name": "collection", - "in": "query", - "description": "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "schema": { - "type": "string" - } - }, - { - "name": "item", - "in": "query", - "description": "Identifier for the actual media object or media file.", - "schema": { - "type": "string" - } - }, - { - "name": "frame", - "in": "query", - "description": "Frame number for media with temporal progression (e.g. video).", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "shot", - "in": "query", - "description": "Shot number for media with temporal progression (e.g. video).", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "timecode", - "in": "query", - "description": "Timecode for media with temporal progression (e.g. video).", - "schema": { - "type": "string" - } - }, - { - "name": "session", - "in": "query", - "description": "Session Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "208": { - "description": "208", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "409": { - "description": "Conflict", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/log/query": { - "post": { - "tags": [ - "Log" - ], - "summary": "Accepts query logs from participants", - "operationId": "postLogQuery", - "parameters": [ - { - "name": "session", - "in": "query", - "description": "Session Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QueryEventLog" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/log/result": { - "post": { - "tags": [ - "Log" - ], - "summary": "Accepts result logs from participants", - "operationId": "postLogResult", - "parameters": [ - { - "name": "session", - "in": "query", - "description": "Session Token", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QueryResultLog" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "LoginRequest": { - "required": [ - "password", - "username" - ], - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, - "UserDetails": { - "required": [ - "id", - "role", - "username" - ], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "username": { - "type": "string" - }, - "role": { - "type": "string", - "enum": [ - "ADMIN", - "JUDGE", - "VIEWER", - "PARTICIPANT" - ] - }, - "sessionId": { - "type": "string" - } - } - }, - "ErrorStatus": { - "required": [ - "description", - "status" - ], - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "status": { - "type": "boolean" - } - } - }, - "SuccessStatus": { - "required": [ - "description", - "status" - ], - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "status": { - "type": "boolean" - } - } - }, - "UserRequest": { - "required": [ - "username" - ], - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - }, - "role": { - "type": "string", - "enum": [ - "ADMIN", - "JUDGE", - "VIEWER", - "PARTICIPANT" - ] - } - } - }, - "SessionId": { - "required": [ - "sessionId" - ], - "type": "object", - "properties": { - "sessionId": { - "type": "string" - } - } - }, - "CurrentTime": { - "required": [ - "timeStamp" - ], - "type": "object", - "properties": { - "timeStamp": { - "type": "integer", - "format": "int64" - } - } - }, - "ClientRunInfo": { - "required": [ - "id", - "name", - "status" - ], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "CREATED", - "ACTIVE", - "PREPARING_TASK", - "RUNNING_TASK", - "TASK_ENDED", - "TERMINATED" - ] - } - } - }, - "ClientRunInfoList": { - "required": [ - "runs" - ], - "type": "object", - "properties": { - "runs": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ClientRunInfo" - } - } - } - }, - "ClientTaskInfo": { - "required": [ - "id", - "name", - "remainingTime", - "running", - "taskGroup" - ], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "taskGroup": { - "type": "string" - }, - "remainingTime": { - "type": "integer", - "format": "int64" - }, - "running": { - "type": "boolean" - } - } - }, - "QueryEvent": { - "required": [ - "category", - "timestamp", - "type", - "value" - ], - "type": "object", - "properties": { - "timestamp": { - "type": "integer", - "format": "int64" - }, - "category": { - "type": "string", - "enum": [ - "TEXT", - "IMAGE", - "SKETCH", - "FILTER", - "BROWSING", - "COOPERATION", - "OTHER" - ] - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "QueryEventLog": { - "required": [ - "events", - "timestamp" - ], - "type": "object", - "properties": { - "timestamp": { - "type": "integer", - "format": "int64" - }, - "events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QueryEvent" - } - } - } - }, - "QueryResult": { - "required": [ - "item" - ], - "type": "object", - "properties": { - "item": { - "type": "string" - }, - "segment": { - "type": "integer", - "format": "int32" - }, - "frame": { - "type": "integer", - "format": "int32" - }, - "score": { - "type": "number", - "format": "double" - }, - "rank": { - "type": "integer", - "format": "int32" - } - } - }, - "QueryResultLog": { - "required": [ - "events", - "resultSetAvailability", - "results", - "sortType", - "timestamp" - ], - "type": "object", - "properties": { - "timestamp": { - "type": "integer", - "format": "int64" - }, - "sortType": { - "type": "string" - }, - "resultSetAvailability": { - "type": "string" - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QueryResult" - } - }, - "events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QueryEvent" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/doc/openapi-v3.json b/doc/openapi-v3.json deleted file mode 100644 index 7b2bfa6c1..000000000 --- a/doc/openapi-v3.json +++ /dev/null @@ -1,3981 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "DRES API", - "description": "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", - "version": "1.0" - }, - "paths": { - "/api/login": { - "post": { - "tags": ["User"], - "summary": "Sets roles for session based on user account and returns a session cookie.", - "operationId": "postApiLogin", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LoginRequest" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/logout": { - "get": { - "tags": ["User"], - "summary": "Clears all user roles of the current session.", - "operationId": "getApiLogout", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/user/list": { - "get": { - "tags": ["User"], - "summary": "Lists all available users.", - "operationId": "getApiUserList", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - } - } - } - } - }, - "/api/user": { - "get": { - "tags": ["User"], - "summary": "Get information about the current user.", - "operationId": "getApiUser", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "post": { - "tags": ["User"], - "summary": "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId": "postApiUser", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserRequest" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "400": { - "description": "If the username is already taken", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/user/{userId}": { - "get": { - "tags": ["User"], - "summary": "Gets details of the user with the given id", - "operationId": "getApiUserWithUserid", - "parameters": [{ - "name": "userId", - "in": "path", - "description": "User's UID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "404": { - "description": "If the user could not be found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "delete": { - "tags": ["User"], - "summary": "Deletes the specified user. Requires ADMIN privileges", - "operationId": "deleteApiUserWithUserid", - "parameters": [{ - "name": "userId", - "in": "path", - "description": "User ID", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "404": { - "description": "If the user could not be found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "patch": { - "tags": ["User"], - "summary": "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", - "operationId": "patchApiUserWithUserid", - "parameters": [{ - "name": "userId", - "in": "path", - "description": "User ID", - "required": true, - "schema": { - "type": "string" - } - }], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserRequest" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/user/session": { - "get": { - "tags": ["User"], - "summary": "Get current sessionId", - "operationId": "getApiUserSession", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionId" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/user/session/active/list": { - "get": { - "tags": ["User"], - "summary": "Get details of all current user sessions", - "operationId": "getApiUserSessionActiveList", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/collection/list": { - "get": { - "tags": ["Collection"], - "summary": "Lists all available media collections with basic information about their content.", - "operationId": "getApiCollectionList", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestMediaCollection" - } - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/collection/{collectionId}": { - "get": { - "tags": ["Collection"], - "summary": "Shows the content of the specified media collection.", - "operationId": "getApiCollectionWithCollectionid", - "parameters": [{ - "name": "collectionId", - "in": "path", - "description": "Collection ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestFullMediaCollection" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "delete": { - "tags": ["Collection"], - "summary": "Deletes a media collection", - "operationId": "deleteApiCollectionWithCollectionid", - "parameters": [{ - "name": "collectionId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/collection": { - "post": { - "tags": ["Collection"], - "summary": "Adds a new media collection", - "operationId": "postApiCollection", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestMediaCollection" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "patch": { - "tags": ["Collection"], - "summary": "Updates a media collection", - "operationId": "patchApiCollection", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestMediaCollection" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/mediaItem": { - "post": { - "tags": ["Collection"], - "summary": "Adds a Media Item to the specified Media Collection.", - "operationId": "postApiMediaitem", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestMediaItem" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "patch": { - "tags": ["Collection"], - "summary": "Updates a Media Item to the specified Media Collection.", - "operationId": "patchApiMediaitem", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestMediaItem" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/mediaItem/{mediaId}": { - "get": { - "tags": ["Collection"], - "summary": "Selects and returns a specific media item.", - "operationId": "getApiMediaitemWithMediaid", - "parameters": [{ - "name": "mediaId", - "in": "path", - "description": "Media item ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestMediaItem" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "delete": { - "tags": ["Collection"], - "summary": "Selects and returns a specific media item.", - "operationId": "deleteApiMediaitemWithMediaid", - "parameters": [{ - "name": "mediaId", - "in": "path", - "description": "Media item ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/collection/{collectionId}/random": { - "get": { - "tags": ["Collection"], - "summary": "Selects and returns a random media item from a given media collection.", - "operationId": "getApiCollectionWithCollectionidRandom", - "parameters": [{ - "name": "collectionId", - "in": "path", - "description": "Collection ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestMediaItem" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/collection/{collectionId}/{startsWith}": { - "get": { - "tags": ["Collection"], - "summary": "Lists media items from a given media collection whose name start with the given string.", - "operationId": "getApiCollectionWithCollectionidWithStartswith", - "parameters": [{ - "name": "collectionId", - "in": "path", - "description": "Collection ID", - "required": true, - "schema": { - "type": "string" - } - }, { - "name": "startsWith", - "in": "path", - "description": "Name starts with", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestMediaItem" - } - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/external/{startsWith}": { - "get": { - "tags": ["Collection"], - "summary": "Lists items from the external media collection whose name start with the given string.", - "operationId": "getApiExternalWithStartswith", - "parameters": [{ - "name": "startsWith", - "in": "path", - "description": "Name starts with.", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/competition/list": { - "get": { - "tags": ["Competition"], - "summary": "Lists an overview of all available competitions with basic information about their content.", - "operationId": "getApiCompetitionList", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CompetitionOverview" - } - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/competition": { - "post": { - "tags": ["Competition"], - "summary": "Creates a new competition.", - "operationId": "postApiCompetition", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CompetitionCreate" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "patch": { - "tags": ["Competition"], - "summary": "Updates an existing competition.", - "operationId": "patchApiCompetition", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestCompetitionDescription" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/competition/{competitionId}": { - "get": { - "tags": ["Competition"], - "summary": "Loads the detailed definition of a specific competition.", - "operationId": "getApiCompetitionWithCompetitionid", - "parameters": [{ - "name": "competitionId", - "in": "path", - "description": "Competition ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestCompetitionDescription" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - }, - "delete": { - "tags": ["Competition"], - "summary": "Deletes the competition with the given competition ID.", - "operationId": "deleteApiCompetitionWithCompetitionid", - "parameters": [{ - "name": "competitionId", - "in": "path", - "description": "Competition ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/competition/{competitionId}/team/list": { - "get": { - "tags": ["Competition"], - "summary": "Lists the Teams of a specific competition.", - "operationId": "getApiCompetitionWithCompetitionidTeamList", - "parameters": [{ - "name": "competitionId", - "in": "path", - "description": "Competition ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Team" - } - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/competition/{competitionId}/team/list/details": { - "get": { - "tags": ["Competition"], - "summary": "Lists the teams with their user details", - "operationId": "getApiCompetitionWithCompetitionidTeamListDetails", - "parameters": [{ - "name": "competitionId", - "in": "path", - "description": "Competition ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestDetailedTeam" - } - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/competition/{competitionId}/task/list": { - "get": { - "tags": ["Competition"], - "summary": "Lists the Tasks of a specific competition.", - "operationId": "getApiCompetitionWithCompetitionidTaskList", - "parameters": [{ - "name": "competitionId", - "in": "path", - "description": "Competition ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestTaskDescription" - } - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/info/list": { - "get": { - "tags": ["Competition Run"], - "summary": "Lists an overview of all competition runs visible to the current user", - "operationId": "getApiRunInfoList", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RunInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/state/list": { - "get": { - "tags": ["Competition Run"], - "summary": "Lists an overview of all competition runs visible to the current user", - "operationId": "getApiRunStateList", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RunState" - } - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/info/{runId}": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns a specific competition run.", - "operationId": "getApiRunInfoWithRunid", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RunInfo" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/state/{runId}": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the state of a specific competition run.", - "operationId": "getApiRunStateWithRunid", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RunState" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/score/{runId}": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the score overviews of a specific competition run.", - "operationId": "getApiRunScoreWithRunid", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ScoreOverview" - } - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/score/{runId}/task": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the overviews of all score boards for the current task run, if it is either running or has just ended.", - "operationId": "getApiRunScoreWithRunidTask", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ScoreOverview" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/hint": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the task hint for the current task run (i.e. the one that is currently selected).", - "operationId": "getApiRunWithRunidHint", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TaskHint" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/target": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId": "getApiRunWithRunidTarget", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TaskTarget" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/task": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the information for the current task (i.e. the one that is currently selected).", - "operationId": "getApiRunWithRunidTask", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TaskInfo" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/task/submission/list": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the submissions for the current task run, if it is either running or has just ended.", - "operationId": "getApiRunWithRunidTaskSubmissionList", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SubmissionInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "oneOf": [{ - "$ref": "#/components/schemas/ErrorStatus" - }, { - "$ref": "#/components/schemas/ErrorStatus" - }] - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/task/submission/list/after/{timestamp}": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.", - "operationId": "getApiRunWithRunidTaskSubmissionListAfterWithTimestamp", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }, { - "name": "timestamp", - "in": "path", - "description": "Minimum Timestamp for returned Submissions", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SubmissionInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/task/submissions/list/task/{taskId}": { - "get": { - "tags": ["Competition Run"], - "summary": "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId": "getApiRunWithRunidTaskSubmissionsListTaskWithTaskid", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }, { - "name": "taskId", - "in": "path", - "description": "Task ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SubmissionInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/create": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Creates a new competition run from an existing competition", - "operationId": "postApiRunAdminCreate", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CompetitionStartMessage" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/start": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Starts a competition run. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidStart", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/task/next": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Moves to and selects the next task. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidTaskNext", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/task/previous": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Moves to and selects the previous task. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidTaskPrevious", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/task/switch/{idx}": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Moves to and selects the specified task. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidTaskSwitchWithIdx", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition run ID", - "required": true, - "schema": { - "type": "string" - } - }, { - "name": "idx", - "in": "path", - "description": "Index of the task to switch to.", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/task/start": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Starts the currently active task as a new task run. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidTaskStart", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/task/abort": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Aborts the currently running task run. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidTaskAbort", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/terminate": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Terminates a competition run. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidTerminate", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/adjust/{duration}": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Adjusts the duration of a running task run. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidAdjustWithDuration", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }, { - "name": "duration", - "in": "path", - "description": "Duration to add.", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/viewer/list": { - "get": { - "tags": ["Competition Run Admin"], - "summary": "Lists all registered viewers for a competition run. This is a method for admins.", - "operationId": "getApiRunAdminWithRunidViewerList", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ViewerInfo" - } - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/admin/{runId}/viewers/{viewerId}/force": { - "post": { - "tags": ["Competition Run Admin"], - "summary": "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId": "postApiRunAdminWithRunidViewersWithVieweridForce", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Competition Run ID", - "required": true, - "schema": { - "type": "string" - } - }, { - "name": "viewerId", - "in": "path", - "description": "Viewer ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/judge/next": { - "get": { - "tags": ["Judgement"], - "summary": "Gets the next open Submission to be judged.", - "operationId": "getApiRunWithRunidJudgeNext", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JudgementRequest" - } - } - } - }, - "202": { - "description": "Accepted", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/judge": { - "post": { - "tags": ["Judgement"], - "summary": "Returns a Judgement.", - "operationId": "postApiRunWithRunidJudge", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Judgement" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/run/{runId}/judge/status": { - "get": { - "tags": ["Judgement"], - "summary": "Gets the status of all judgement validators.", - "operationId": "getApiRunWithRunidJudgeStatus", - "parameters": [{ - "name": "runId", - "in": "path", - "description": "Run ID", - "required": true, - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/JudgementValidatorStatus" - } - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/submit": { - "get": { - "tags": ["Submission"], - "summary": "Endpoint to accept submissions", - "operationId": "getSubmit", - "parameters": [{ - "name": "collection", - "in": "query", - "description": "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "schema": { - "type": "string" - } - }, { - "name": "item", - "in": "query", - "description": "Identifier for the actual media object or media file.", - "schema": { - "type": "string" - } - }, { - "name": "frame", - "in": "query", - "description": "Frame number for media with temporal progression (e.g. video).", - "schema": { - "type": "integer", - "format": "int32" - } - }, { - "name": "shot", - "in": "query", - "description": "Shot number for media with temporal progression (e.g. video).", - "schema": { - "type": "integer", - "format": "int32" - } - }, { - "name": "timecode", - "in": "query", - "description": "Timecode for media with temporal progression (e.g. video).", - "schema": { - "type": "string" - } - }], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "208": { - "description": "208", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "409": { - "description": "Conflict", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/log/query": { - "post": { - "tags": ["Log"], - "summary": "Accepts query logs from participants", - "operationId": "postLogQuery", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QueryEventLog" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/log/result": { - "post": { - "tags": ["Log"], - "summary": "Accepts result logs from participants", - "operationId": "postLogResult", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QueryResultLog" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SuccessStatus" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "LoginRequest": { - "required": ["password", "username"], - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, - "SuccessStatus": { - "required": ["description", "status"], - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "status": { - "type": "boolean" - } - } - }, - "ErrorStatus": { - "required": ["description", "status"], - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "status": { - "type": "boolean" - } - } - }, - "UserDetails": { - "required": ["id", "role", "username"], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "username": { - "type": "string" - }, - "role": { - "type": "string", - "enum": ["ADMIN", "JUDGE", "VIEWER", "PARTICIPANT"] - }, - "sessionId": { - "type": "string" - } - } - }, - "UserRequest": { - "required": ["username"], - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - }, - "role": { - "type": "string", - "enum": ["ADMIN", "JUDGE", "VIEWER", "PARTICIPANT"] - } - } - }, - "SessionId": { - "required": ["sessionId"], - "type": "object", - "properties": { - "sessionId": { - "type": "string" - } - } - }, - "RestMediaCollection": { - "required": ["id", "name"], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "basePath": { - "type": "string" - } - } - }, - "RestFullMediaCollection": { - "required": ["collection", "items"], - "type": "object", - "properties": { - "collection": { - "$ref": "#/components/schemas/RestMediaCollection" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestMediaItem" - } - } - } - }, - "RestMediaItem": { - "required": ["collectionId", "id", "location", "name", "type"], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["IMAGE", "VIDEO"] - }, - "collectionId": { - "type": "string" - }, - "location": { - "type": "string" - }, - "durationMs": { - "type": "integer", - "format": "int64" - }, - "fps": { - "type": "number", - "format": "float" - } - } - }, - "CompetitionOverview": { - "required": ["description", "id", "name", "taskCount", "teamCount"], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "taskCount": { - "type": "integer", - "format": "int32" - }, - "teamCount": { - "type": "integer", - "format": "int32" - } - } - }, - "CompetitionCreate": { - "required": ["description", "name", "participantsCanView"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "participantsCanView": { - "type": "boolean" - } - } - }, - "RestCompetitionDescription": { - "required": ["id", "name", "participantCanView", "taskGroups", "taskTypes", "tasks", "teams"], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "taskTypes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TaskType" - } - }, - "taskGroups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TaskGroup" - } - }, - "tasks": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestTaskDescription" - } - }, - "teams": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestTeam" - } - }, - "participantCanView": { - "type": "boolean" - } - } - }, - "RestTaskDescription": { - "required": ["components", "duration", "id", "mediaCollectionId", "name", "target", "taskGroup", "taskType"], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "taskGroup": { - "type": "string" - }, - "taskType": { - "type": "string" - }, - "duration": { - "type": "integer", - "format": "int64" - }, - "mediaCollectionId": { - "type": "string" - }, - "target": { - "$ref": "#/components/schemas/RestTaskDescriptionTarget" - }, - "components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestTaskDescriptionComponent" - } - } - } - }, - "RestTaskDescriptionComponent": { - "required": ["type"], - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO"] - }, - "start": { - "type": "integer", - "format": "int64" - }, - "end": { - "type": "integer", - "format": "int64" - }, - "description": { - "type": "string" - }, - "path": { - "type": "string" - }, - "dataType": { - "type": "string" - }, - "mediaItem": { - "type": "string" - }, - "range": { - "$ref": "#/components/schemas/TemporalRange" - } - } - }, - "RestTaskDescriptionTarget": { - "required": ["mediaItems", "type"], - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "MULTIPLE_MEDIA_ITEMS", "JUDGEMENT"] - }, - "mediaItems": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestTaskDescriptionTargetItem" - } - } - } - }, - "RestTaskDescriptionTargetItem": { - "required": ["mediaItem"], - "type": "object", - "properties": { - "mediaItem": { - "type": "string" - }, - "temporalRange": { - "$ref": "#/components/schemas/TemporalRange" - } - } - }, - "RestTeam": { - "required": ["color", "logo", "name", "users"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "color": { - "type": "string" - }, - "logo": { - "type": "string" - }, - "users": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "TaskGroup": { - "required": ["name", "type"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "TaskType": { - "required": ["components", "filter", "name", "options", "score", "targetType", "taskDuration"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "taskDuration": { - "type": "integer", - "format": "int64" - }, - "targetType": { - "type": "string", - "enum": ["SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "MULTIPLE_MEDIA_ITEMS", "JUDGEMENT"] - }, - "components": { - "uniqueItems": true, - "type": "array", - "items": { - "type": "string", - "enum": ["IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO"] - } - }, - "score": { - "type": "string", - "enum": ["KIS", "AVS"] - }, - "filter": { - "uniqueItems": true, - "type": "array", - "items": { - "type": "string", - "enum": ["NO_DUPLICATES", "ONE_CORRECT_PER_TEAM", "TEMPORAL_SUBMISSION"] - } - }, - "options": { - "uniqueItems": true, - "type": "array", - "items": { - "type": "string", - "enum": ["HIDDEN_RESULTS", "MAP_TO_SEGMENT"] - } - } - } - }, - "TemporalPoint": { - "required": ["unit", "value"], - "type": "object", - "properties": { - "value": { - "type": "number", - "format": "double" - }, - "unit": { - "type": "string", - "enum": ["FRAME_NUMBER", "SECONDS", "MILLISECONDS"] - } - } - }, - "TemporalRange": { - "required": ["end", "start"], - "type": "object", - "properties": { - "start": { - "$ref": "#/components/schemas/TemporalPoint" - }, - "end": { - "$ref": "#/components/schemas/TemporalPoint" - } - } - }, - "Team": { - "required": ["color", "logo", "name", "users"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "color": { - "type": "string" - }, - "logo": { - "type": "string" - }, - "users": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UID" - } - } - } - }, - "UID": { - "required": ["string"], - "type": "object", - "properties": { - "string": { - "type": "string" - } - } - }, - "RestDetailedTeam": { - "required": ["color", "logo", "name", "users"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "color": { - "type": "string" - }, - "logo": { - "type": "string" - }, - "users": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserDetails" - } - } - } - }, - "RunInfo": { - "required": ["competitionId", "id", "name", "participantsCanView", "tasks", "teams"], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "teams": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TeamInfo" - } - }, - "tasks": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TaskInfo" - } - }, - "competitionId": { - "type": "string" - }, - "participantsCanView": { - "type": "boolean" - } - } - }, - "TaskInfo": { - "required": ["duration", "name", "taskGroup"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "taskGroup": { - "$ref": "#/components/schemas/TaskGroup" - }, - "duration": { - "type": "integer", - "format": "int64" - } - } - }, - "TeamInfo": { - "required": ["color", "logo", "name"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "color": { - "type": "string" - }, - "logo": { - "type": "string" - } - } - }, - "RunState": { - "required": ["id", "status", "timeLeft"], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "status": { - "type": "string", - "enum": ["CREATED", "ACTIVE", "PREPARING_TASK", "RUNNING_TASK", "TASK_ENDED", "TERMINATED"] - }, - "currentTask": { - "$ref": "#/components/schemas/TaskInfo" - }, - "timeLeft": { - "type": "integer", - "format": "int64" - } - } - }, - "Score": { - "required": ["score", "teamId"], - "type": "object", - "properties": { - "teamId": { - "type": "integer", - "format": "int32" - }, - "score": { - "type": "number", - "format": "double" - } - } - }, - "ScoreOverview": { - "required": ["name", "scores"], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "taskGroup": { - "type": "string" - }, - "scores": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Score" - } - } - } - }, - "ContentElement": { - "required": ["contentType", "offset"], - "type": "object", - "properties": { - "contentType": { - "type": "string", - "enum": ["TEXT", "VIDEO", "IMAGE"] - }, - "content": { - "type": "string" - }, - "offset": { - "type": "integer", - "format": "int64" - } - } - }, - "TaskHint": { - "required": ["loop", "sequence", "taskId"], - "type": "object", - "properties": { - "taskId": { - "type": "string" - }, - "sequence": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContentElement" - } - }, - "loop": { - "type": "boolean" - } - } - }, - "TaskTarget": { - "required": ["sequence", "taskId"], - "type": "object", - "properties": { - "taskId": { - "type": "string" - }, - "sequence": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContentElement" - } - } - } - }, - "SubmissionInfo": { - "required": ["member", "status", "team", "timestamp"], - "type": "object", - "properties": { - "team": { - "type": "integer", - "format": "int32" - }, - "member": { - "type": "string" - }, - "status": { - "type": "string", - "enum": ["CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE"] - }, - "timestamp": { - "type": "integer", - "format": "int64" - }, - "id": { - "type": "string" - }, - "item": { - "$ref": "#/components/schemas/RestMediaItem" - }, - "start": { - "type": "integer", - "format": "int64" - }, - "end": { - "type": "integer", - "format": "int64" - } - } - }, - "CompetitionStartMessage": { - "required": ["competitionId", "name", "scoreboards", "type"], - "type": "object", - "properties": { - "competitionId": { - "type": "string" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["SYNCHRONOUS", "ASYNCHRONOUS"] - }, - "scoreboards": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "ViewerInfo": { - "required": ["host", "ready", "username", "viewersId"], - "type": "object", - "properties": { - "viewersId": { - "type": "string" - }, - "username": { - "type": "string" - }, - "host": { - "type": "string" - }, - "ready": { - "type": "boolean" - } - } - }, - "JudgementRequest": { - "required": ["collection", "item", "taskDescription", "token", "validator"], - "type": "object", - "properties": { - "token": { - "type": "string" - }, - "validator": { - "type": "string" - }, - "collection": { - "type": "string" - }, - "item": { - "type": "string" - }, - "taskDescription": { - "type": "string" - }, - "startTime": { - "type": "string" - }, - "endTime": { - "type": "string" - } - } - }, - "Judgement": { - "required": ["token", "validator", "verdict"], - "type": "object", - "properties": { - "token": { - "type": "string" - }, - "validator": { - "type": "string" - }, - "verdict": { - "type": "string", - "enum": ["CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE"] - } - } - }, - "JudgementValidatorStatus": { - "required": ["open", "pending", "validator"], - "type": "object", - "properties": { - "validator": { - "type": "string" - }, - "pending": { - "type": "integer", - "format": "int32" - }, - "open": { - "type": "integer", - "format": "int32" - } - } - }, - "QueryEvent": { - "required": ["category", "timestamp", "type", "value"], - "type": "object", - "properties": { - "timestamp": { - "type": "integer", - "format": "int64" - }, - "category": { - "type": "string" - }, - "type": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "string" - } - } - }, - "QueryEventLog": { - "required": ["events", "serverTimeStamp$backend", "timestamp", "type"], - "type": "object", - "properties": { - "timestamp": { - "type": "integer", - "format": "int64" - }, - "events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QueryEvent" - } - }, - "type": { - "type": "string" - }, - "serverTimeStamp": { - "type": "integer", - "format": "int64", - "writeOnly": true - }, - "serverTimeStamp$backend": { - "type": "integer", - "format": "int64" - } - } - }, - "QueryResult": { - "required": ["video"], - "type": "object", - "properties": { - "video": { - "type": "string" - }, - "shot": { - "type": "integer", - "format": "int32" - }, - "frame": { - "type": "integer", - "format": "int32" - }, - "score": { - "type": "number", - "format": "double" - }, - "rank": { - "type": "integer", - "format": "int32" - } - } - }, - "QueryResultLog": { - "required": ["resultSetAvailability", "results", "serverTimeStamp$backend", "sortType", "timestamp", "usedCategories", "usedTypes", "values"], - "type": "object", - "properties": { - "timestamp": { - "type": "integer", - "format": "int64" - }, - "values": { - "type": "array", - "items": { - "type": "string" - } - }, - "usedCategories": { - "type": "array", - "items": { - "type": "string" - } - }, - "usedTypes": { - "type": "array", - "items": { - "type": "string" - } - }, - "sortType": { - "type": "array", - "items": { - "type": "string" - } - }, - "resultSetAvailability": { - "type": "string" - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QueryResult" - } - }, - "serverTimeStamp": { - "type": "integer", - "format": "int64", - "writeOnly": true - }, - "serverTimeStamp$backend": { - "type": "integer", - "format": "int64" - } - } - } - } - } -} \ No newline at end of file From 37f7fa4ac6c96785dac2f6b43b23fb6ca3181415 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 3 Mar 2021 16:53:58 +0100 Subject: [PATCH 39/95] Logic for #163 in place and appears to be working UI needs some cleanup --- .../judged/BasicJudgementValidator.kt | 5 ++--- .../validation/judged/BasicVoteValidator.kt | 12 ++++++++--- .../judgement-voting-viewer.component.html | 5 +++-- .../judgement-voting-viewer.component.ts | 20 +++++++++++++++---- .../services/session/authentication.sevice.ts | 4 ++-- .../src/app/user/profile/profile.component.ts | 2 +- 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 40f3fd704..2d41d407a 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -125,13 +125,12 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * @param verdict The verdict of the judge. */ override fun judge(token: String, verdict: SubmissionStatus) { - doJudge(token, verdict) + processSubmission(token, verdict)?.status = verdict } - internal fun doJudge(token: String, verdict: SubmissionStatus) : Submission? = updateLock.write { + internal fun processSubmission(token: String, verdict: SubmissionStatus) : Submission? = updateLock.write { require(this.waiting.containsKey(token)) { "This JudgementValidator does not contain a submission for the token '$token'." } val submission = this.waiting[token] ?: return@write null //submission with token not found TODO: this should be logged - submission.status = verdict //add to cache this.cache[ItemRange(submission)] = verdict diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index 0e3c86a5f..b97ddd032 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -67,10 +67,16 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() //siphon of undecidable submission from logic of super class override fun judge(token: String, verdict: SubmissionStatus) { - val submission = super.doJudge(token, verdict) + val submission = super.processSubmission(token, verdict) + + if (submission != null) { + when(verdict){ + SubmissionStatus.CORRECT, + SubmissionStatus.WRONG -> submission.status = verdict + SubmissionStatus.INDETERMINATE -> {} + SubmissionStatus.UNDECIDABLE -> submissionQueue.add(submission) + } - if (submission != null && verdict == SubmissionStatus.UNDECIDABLE) { - submissionQueue.add(submission) } } diff --git a/frontend/src/app/judgement/judgement-voting-viewer.component.html b/frontend/src/app/judgement/judgement-voting-viewer.component.html index 419bdb34c..58df29afe 100644 --- a/frontend/src/app/judgement/judgement-voting-viewer.component.html +++ b/frontend/src/app/judgement/judgement-voting-viewer.component.html @@ -12,9 +12,10 @@

Vote for Submission

- + + +
diff --git a/frontend/src/app/judgement/judgement-voting-viewer.component.ts b/frontend/src/app/judgement/judgement-voting-viewer.component.ts index 8ba1244ac..919705d58 100644 --- a/frontend/src/app/judgement/judgement-voting-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-voting-viewer.component.ts @@ -1,4 +1,4 @@ -import {Component, Input, OnInit} from '@angular/core'; +import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core'; import {BehaviorSubject, interval, Observable, of, Subscription} from 'rxjs'; import {JudgementRequest, JudgementService} from '../../../openapi'; import {catchError, filter, map, switchMap, withLatestFrom} from 'rxjs/operators'; @@ -6,15 +6,16 @@ import {ActivatedRoute, Router} from '@angular/router'; import {AppConfig} from '../app.config'; import {HttpErrorResponse, HttpResponse} from '@angular/common/http'; import {MatSnackBar} from '@angular/material/snack-bar'; +import {JudgementMediaViewerComponent} from './judgement-media-viewer.component'; @Component({ selector: 'app-judgement-voting-viewer', templateUrl: './judgement-voting-viewer.component.html', styleUrls: ['./judgement-voting-viewer.component.scss'] }) -export class JudgementVotingViewerComponent implements OnInit { +export class JudgementVotingViewerComponent implements OnInit, OnDestroy { - @Input() pollingFrequency = 10_000; + @Input() pollingFrequency = 1000; private runId: Observable; private requestSub: Subscription; @@ -25,6 +26,8 @@ export class JudgementVotingViewerComponent implements OnInit { observableJudgementRequest: BehaviorSubject = new BehaviorSubject(null); voteClientPath: Observable; + @ViewChild(JudgementMediaViewerComponent) judgePlayer: JudgementMediaViewerComponent; + constructor( private judgementService: JudgementService, private activeRoute: ActivatedRoute, @@ -48,12 +51,13 @@ export class JudgementVotingViewerComponent implements OnInit { if (req.status === 202) { this.isJudgmentAvailable = false; this.judgementRequest = null; + this.judgePlayer.stop(); console.log('currently nothing for audience to vote on'); return null; } else { const lastRequest = req.body; if (this.judgementRequest !== null && lastRequest.token === this.judgementRequest.token) { - return of(null); // still the same, no action required + return null; // still the same, no action required } return lastRequest; } @@ -87,4 +91,12 @@ export class JudgementVotingViewerComponent implements OnInit { }); } + ngOnDestroy(): void { + this.requestSub.unsubscribe(); + this.requestSub = null; + if (this.judgePlayer) { + this.judgePlayer.stop(); + } + } + } diff --git a/frontend/src/app/services/session/authentication.sevice.ts b/frontend/src/app/services/session/authentication.sevice.ts index ee0b66947..a6f3a9454 100644 --- a/frontend/src/app/services/session/authentication.sevice.ts +++ b/frontend/src/app/services/session/authentication.sevice.ts @@ -18,7 +18,7 @@ export class AuthenticationService { * Constructor */ constructor(@Inject(UserService) private userService: UserService) { - this.userService.getApiUserSession('').pipe( + this.userService.getApiUserSession().pipe( catchError(e => of(null)), filter(s => s != null), flatMap(s => this.userService.getApiUser()), @@ -61,7 +61,7 @@ export class AuthenticationService { * Tries to logout the current user. Returns an Observable! */ public logout() { - return this.userService.getApiLogout('').pipe( + return this.userService.getApiLogout().pipe( catchError(e => of(null)), tap(() => { this.userDetails.next(null); diff --git a/frontend/src/app/user/profile/profile.component.ts b/frontend/src/app/user/profile/profile.component.ts index 181b62a61..82efff430 100644 --- a/frontend/src/app/user/profile/profile.component.ts +++ b/frontend/src/app/user/profile/profile.component.ts @@ -36,7 +36,7 @@ export class ProfileComponent implements OnInit, OnDestroy { ) { this.user = this.authenticationService.user; this.loggedIn = this.authenticationService.isLoggedIn; - this.sessionId = this.userService.getApiUserSession(''); + this.sessionId = this.userService.getApiUserSession(); } ngOnInit(): void { From 766575dce019acb968395e980882243a5464c143 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 4 Mar 2021 20:47:26 +0100 Subject: [PATCH 40/95] Added TaskScoreListCSVHandler, addresses #99 --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 1 + .../rest/handler/CompetitionRunScoreHandler.kt | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) 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 bb38460ac..cad6654e4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -113,6 +113,7 @@ object RestApi { HistoryTaskScoreHandler(), ListScoreSeriesHandler(), ListScoreboardsHandler(), + TaskScoreListCSVHandler(), // Competition run admin CreateCompetitionRunAdminHandler(dataAccessLayer.competitions, dataAccessLayer.collections, config), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index a64d06fea..7ed7fd8a1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -177,6 +177,24 @@ class HistoryTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler { + + override val route = "score/run/:runId/tasks/csv" + + override fun doGet(ctx: Context): String { + + val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() + val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) + val rac = RunActionContext.runActionContext(ctx, run) + + return "task,group,team,score\n" + run.tasks(rac).filter { it.started != null}.sortedBy { it.started }.flatMap { task -> + task.scorer.scores().map { "${task.taskDescription.name},${task.taskDescription.taskGroup.name},${run.competitionDescription.teams.find { t -> t.uid == it.key }?.name ?: "???"},${it.value}" } + }.joinToString(separator = "\n") + + } + +} + /** * A [GetRestHandler] that returns the names of all available scoreboards for a given run. */ From 42a6d7ad9bd3573160a526aecb93636dcfee6837 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 5 Mar 2021 20:14:03 +0100 Subject: [PATCH 41/95] Did some refactoring along the line of Competition and Task implementation and started implementation class InteractiveAsynchronousCompetition. --- .../dev/dres/api/cli/CompetitionRunCommand.kt | 28 ++-- .../rest/handler/AbstractPreviewHandler.kt | 2 +- .../rest/handler/BatchSubmissionHandler.kt | 26 ++- .../handler/CompetitionRunAdminHandler.kt | 24 +-- .../CompetitionRunClientInfoHandler.kt | 4 +- .../handler/CompetitionRunScoreHandler.kt | 10 +- .../dres/api/rest/handler/JudgementHandler.kt | 8 +- .../api/rest/handler/SubmissionHandler.kt | 11 +- .../api/rest/types/audit/RestAuditLogEntry.kt | 2 +- .../dev/dres/api/rest/types/run/RunInfo.kt | 4 +- .../dev/dres/api/rest/types/run/RunState.kt | 4 +- .../dres/api/rest/types/run/SubmissionInfo.kt | 6 +- .../dev/dres/api/rest/types/run/TaskInfo.kt | 4 +- .../dev/dres/api/rest/types/run/TeamInfo.kt | 4 +- .../dev/dres/data/dbo/DataAccessLayer.kt | 7 +- .../{Task.kt => AbstractInteractiveTask.kt} | 76 +++------ .../model/run/AbstractNonInteractiveTask.kt | 30 ++++ .../dev/dres/data/model/run/AbstractRun.kt | 43 +++++ .../dev/dres/data/model/run/CompetitionRun.kt | 45 ------ .../run/InteractiveAsynchronousCompetition.kt | 112 +++++++++++++ .../InteractiveAsynchronousCompetitionRun.kt | 54 ------- .../run/InteractiveSynchronousCompetition.kt | 113 +++++++++++++ .../InteractiveSynchronousCompetitionRun.kt | 149 ------------------ .../model/run/NonInteractiveCompetition.kt | 47 ++++++ .../model/run/NonInteractiveCompetitonRun.kt | 40 ----- .../dres/data/model/run/RunActionContext.kt | 8 +- .../dev/dres/data/model/run/Submission.kt | 141 ----------------- .../data/model/run/interfaces/Competition.kt | 24 +++ .../dev/dres/data/model/run/interfaces/Ids.kt | 10 ++ .../data/model/run/{ => interfaces}/Run.kt | 20 ++- .../dres/data/model/run/interfaces/Task.kt | 29 ++++ .../dres/data/model/submissions/Submission.kt | 58 +++++++ .../{run => submissions}/SubmissionStatus.kt | 2 +- .../aspects/BaseSubmissionAspect.kt | 13 ++ .../model/submissions/aspects/ItemAspect.kt | 12 ++ .../model/submissions/aspects/OriginAspect.kt | 14 ++ .../aspects/SpatialSubmissionAspect.kt | 8 + .../model/submissions/aspects/StatusAspect.kt | 12 ++ .../submissions/aspects/TemporalAspect.kt | 19 +++ .../aspects/TemporalSubmissionAspect.kt | 8 + .../submissions/batch/BaseResultBatch.kt | 14 ++ .../batch/BaseResultBatchElement.kt | 11 ++ .../submissions/batch/BaseSubmissionBatch.kt | 15 ++ .../submissions/batch/ItemBatchElement.kt | 13 ++ .../model/submissions/batch/ResultBatch.kt | 14 ++ .../submissions/batch/SubmissionBatch.kt | 12 ++ .../submissions/batch/TemporalBatchElement.kt | 19 +++ .../batch/TemporalSubmissionBatch.kt | 15 ++ .../serializers/AuditLogEntrySerializer.kt | 2 +- .../serializers/CompetitionRunSerializer.kt | 30 ++-- .../data/serializers/SubmissionSerializer.kt | 26 +-- .../run/InteractiveAsynchronousRunManager.kt | 27 +++- .../dev/dres/run/InteractiveRunManager.kt | 23 +-- .../run/InteractiveSynchronousRunManager.kt | 66 ++++---- ...Manager.kt => NonInteractiveRunManager.kt} | 14 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 18 +-- .../main/kotlin/dev/dres/run/RunManager.kt | 25 +-- .../dev/dres/run/audit/AuditLogEntry.kt | 4 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 4 +- .../dev/dres/run/eventstream/StreamEvent.kt | 2 +- .../handlers/SubmissionStatisticsHandler.kt | 4 +- .../handlers/TeamCombinationScoreHandler.kt | 8 +- .../dres/run/filter/AllSubmissionFilter.kt | 3 +- .../run/filter/CorrectSubmissionFilter.kt | 4 +- .../run/filter/DuplicateSubmissionFilter.kt | 4 +- .../dres/run/filter/SubmissionCountFilter.kt | 4 +- .../dev/dres/run/filter/SubmissionFilter.kt | 8 +- .../run/filter/TemporalSubmissionFilter.kt | 6 +- .../interfaces/IncrementalTaskRunScorer.kt | 2 +- .../interfaces/RecalculatingTaskRunScorer.kt | 6 +- .../scoreboard/MaxNormalizingScoreBoard.kt | 9 +- .../dres/run/score/scoreboard/Scoreboard.kt | 7 +- .../scoreboard/SumAggregateScoreBoard.kt | 7 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 9 +- .../dres/run/score/scorer/KisTaskScorer.kt | 4 +- .../dres/run/updatables/EndTaskUpdatable.kt | 6 +- .../run/updatables/ScoreboardsUpdatable.kt | 7 +- .../dres/run/updatables/ScoresUpdatable.kt | 20 +-- .../validation/ChainedSubmissionValidator.kt | 4 +- .../MediaItemsSubmissionValidator.kt | 4 +- .../TemporalContainmentSubmissionValidator.kt | 6 +- .../TemporalOverlapSubmissionValidator.kt | 6 +- .../interfaces/JudgementValidator.kt | 5 +- .../interfaces/SubmissionBatchValidator.kt | 4 +- .../SubmissionJudgementValidator.kt | 4 +- .../interfaces/SubmissionValidator.kt | 4 +- .../validation/interfaces/VoteValidator.kt | 4 +- .../judged/BasicJudgementValidator.kt | 4 +- .../validation/judged/BasicVoteValidator.kt | 6 +- .../dres/run/validation/judged/ItemRange.kt | 4 +- .../run/score/scorer/AvsTaskScorerTest.kt | 2 +- .../run/score/scorer/KisTaskScorerTest.kt | 2 +- 92 files changed, 993 insertions(+), 769 deletions(-) rename backend/src/main/kotlin/dev/dres/data/model/run/{Task.kt => AbstractInteractiveTask.kt} (52%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/AbstractRun.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/CompetitionRun.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetitionRun.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetitionRun.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetitonRun.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/Submission.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt rename backend/src/main/kotlin/dev/dres/data/model/run/{ => interfaces}/Run.kt (66%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt rename backend/src/main/kotlin/dev/dres/data/model/{run => submissions}/SubmissionStatus.kt (89%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/BaseSubmissionAspect.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/SpatialSubmissionAspect.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/StatusAspect.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalSubmissionAspect.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatchElement.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/SubmissionBatch.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt rename backend/src/main/kotlin/dev/dres/run/{NonInteractiveSynchronousRunManager.kt => NonInteractiveRunManager.kt} (93%) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt index 327102f09..0353d7d7f 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt @@ -9,10 +9,10 @@ import com.github.ajalt.clikt.parameters.options.* import com.jakewharton.picnic.table import dev.dres.data.dbo.DAO import dev.dres.data.model.UID -import dev.dres.data.model.run.CompetitionRun -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.run.interfaces.Competition +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.utilities.extensions.UID @@ -20,7 +20,7 @@ import java.nio.file.Files import java.nio.file.Paths import java.nio.file.StandardOpenOption -class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktCommand(name = "run") { +class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktCommand(name = "run") { init { subcommands(OngoingCompetitionRunsCommand(), ListCompetitionRunsCommand(), DeleteRunCommand(), ExportRunCommand(), CompetitionRunsHistoryCommand(), ResetSubmissionStatusCommand()) @@ -85,7 +85,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktC override fun run() { if (plain) { this@CompetitionRunCommand.runs.forEach { - println("${RunSummary(it.id.string, it.name, it.competitionDescription.description, if (it is InteractiveSynchronousCompetitionRun) it.lastTask?.taskDescription?.name + println("${RunSummary(it.id.string, it.name, it.description.description, if (it is InteractiveSynchronousCompetition) it.lastTask?.description?.name ?: "N/A" else "N/A")}") } } else { @@ -111,7 +111,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktC }else{ "unkown" } - row(it.id, it.name, it.competitionDescription.description, if (it is InteractiveSynchronousCompetitionRun) it.lastTask?.taskDescription?.name + row(it.id, it.name, it.description.description, if (it is InteractiveSynchronousCompetition) it.lastTask?.description?.name ?: "N/A" else "N/A", status) } } @@ -203,22 +203,22 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktC println(it.name) println("Teams:") - it.competitionDescription.teams.forEach { + it.description.teams.forEach { println(it) } println() println("All Tasks:") - it.competitionDescription.tasks.forEach { + it.description.tasks.forEach { println(it) } println() println("Evaluated Tasks:") it.tasks.forEach {t -> - println(t.taskDescription) + println(t.description) - if (t is InteractiveSynchronousCompetitionRun.TaskRun){ + if (t is InteractiveSynchronousCompetition.Task){ println("Submissions") t.submissions.forEach { println(it) } } @@ -253,7 +253,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktC return } - if (run is InteractiveSynchronousCompetitionRun) { + if (run is InteractiveSynchronousCompetition) { /* Fetch submissions and reset them. */ val submissions = run.tasks.flatMap { @@ -286,7 +286,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktC return } - if (run is InteractiveSynchronousCompetitionRun) { + if (run is InteractiveSynchronousCompetition) { /* Fetch submissions and reset them. */ val submissions = run.tasks.filter { it.uid.string in ids @@ -316,10 +316,10 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktC return } - if (run is InteractiveSynchronousCompetitionRun) { + if (run is InteractiveSynchronousCompetition) { val submissions = - run.tasks.filter { it.taskDescription.taskGroup.name == taskGroup }.flatMap { it.submissions } + run.tasks.filter { it.description.taskGroup.name == taskGroup }.flatMap { it.submissions } submissions.forEach { it.status = SubmissionStatus.INDETERMINATE } this@CompetitionRunCommand.runs.update(run) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt index 8073ef961..a835593b5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt @@ -8,7 +8,7 @@ import dev.dres.data.model.Config import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.run.TemporalSubmissionAspect +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.utilities.FFmpegUtil diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt index 35bd04ce0..f421082aa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt @@ -12,7 +12,9 @@ import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.basics.media.MediaItemSegmentList import dev.dres.data.model.run.* -import dev.dres.run.NonInteractiveSynchronousRunManager +import dev.dres.data.model.submissions.batch.* +import dev.dres.run.InteractiveRunManager +import dev.dres.run.NonInteractiveRunManager import dev.dres.utilities.TimeUtil import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId @@ -30,9 +32,11 @@ abstract class BatchSubmissionHandler(internal val collections: DAO().find { it.id == runId } + protected fun getInteractiveManager(userId: UID, runId: UID): InteractiveRunManager? + = AccessManager.getRunManagerForUser(userId).filterIsInstance().find { it.id == runId } + protected fun getNonInteractiveManager(userId: UID, runId: UID): NonInteractiveRunManager? + = AccessManager.getRunManagerForUser(userId).filterIsInstance().find { it.id == runId } } data class JsonBatchSubmission(val batches : List) @@ -63,7 +67,7 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D val userId = userId(ctx) val runId = runId(ctx) - val runManager = getRelevantManager(userId, runId) ?: throw ErrorStatusException(404, "Run ${runId.string} not found", ctx) + val runManager = getNonInteractiveManager(userId, runId) ?: throw ErrorStatusException(404, "Run ${runId.string} not found", ctx) val rac = RunActionContext.runActionContext(ctx, runManager) @@ -78,28 +82,22 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D }?.uid ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) val resultBatches = jsonBatch.batches.mapNotNull { batch -> - - val task = runManager.tasks(rac).find { it.taskDescription.name == batch.taskName } ?: return@mapNotNull null - val mediaCollectionId = task.taskDescription.mediaCollectionId - + val task = runManager.tasks(rac).find { it.description.name == batch.taskName } ?: return@mapNotNull null + val mediaCollectionId = task.description.mediaCollectionId val results = batch.results.map { result -> - val mediaItem = this.itemIndex[mediaCollectionId to result.item].first() //TODO deal with invalid name return@map if (mediaItem is MediaItem.VideoItem && result.segment != null) { val segmentList = segmentIndex[mediaItem.id].first() val time = TimeUtil.shotToTime(result.segment.toString(), mediaItem, segmentList)!! - TemporalBatchElement(mediaItem, time.first, time.second) - } else { ItemBatchElement(mediaItem) } - } if (results.all { it is TemporalBatchElement }) { @Suppress("UNCHECKED_CAST") - BaseResultBatch(mediaCollectionId, batch.resultName, results as List) + (BaseResultBatch(mediaCollectionId, batch.resultName, results as List)) } else { BaseResultBatch(mediaCollectionId, batch.resultName, results) } @@ -108,7 +106,7 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D val submissionBatch = if (resultBatches.all { it.results.first() is TemporalBatchElement }) { @Suppress("UNCHECKED_CAST") - TemporalSubmissionBatch(team, userId, UID(), resultBatches as List>) + (TemporalSubmissionBatch(team, userId, UID(), resultBatches as List>)) } else { BaseSubmissionBatch(team, userId, UID(), resultBatches as List>) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index c008c5321..2075b8d2d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -13,7 +13,7 @@ import dev.dres.data.model.Config import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.* import dev.dres.run.audit.AuditLogger @@ -48,7 +48,7 @@ abstract class AbstractCompetitionRunAdminRestHandler(override val permittedRole } /** - * REST handler to create a [InteractiveSynchronousCompetitionRun]. + * REST handler to create a [InteractiveSynchronousCompetition]. */ class CreateCompetitionRunAdminHandler(private val competitions: DAO, private val collections: DAO, config: Config) : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { @@ -131,7 +131,7 @@ class CreateCompetitionRunAdminHandler(private val competitions: DAO { override val route: String = "run/admin/:runId/start" @@ -167,7 +167,7 @@ class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler() } /** - * REST handler to move to the next task in a [InteractiveSynchronousCompetitionRun]. + * REST handler to move to the next task in a [InteractiveSynchronousCompetition]. */ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { override val route: String = "run/admin/:runId/task/next" @@ -205,7 +205,7 @@ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandle } /** - * REST handler to move to the next task in a [InteractiveSynchronousCompetitionRun]. + * REST handler to move to the next task in a [InteractiveSynchronousCompetition]. */ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { override val route: String = "run/admin/:runId/task/switch/:idx" @@ -248,7 +248,7 @@ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHand } /** - * REST handler to move to the previous task in a [InteractiveSynchronousCompetitionRun]. + * REST handler to move to the previous task in a [InteractiveSynchronousCompetition]. */ class PreviousTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/:runId/task/previous" @@ -284,7 +284,7 @@ class PreviousTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHa } /** - * REST handler to start the current task in a [InteractiveSynchronousCompetitionRun]. + * REST handler to start the current task in a [InteractiveSynchronousCompetition]. */ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { override val route: String = "run/admin/:runId/task/start" @@ -319,7 +319,7 @@ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl } /** - * REST handler to abort the current task in a [InteractiveSynchronousCompetitionRun]. + * REST handler to abort the current task in a [InteractiveSynchronousCompetition]. */ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { override val route: String = "run/admin/:runId/task/abort" @@ -354,7 +354,7 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl } /** - * REST handler to terminate a [InteractiveSynchronousCompetitionRun]. + * REST handler to terminate a [InteractiveSynchronousCompetition]. */ class TerminateCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/:runId/terminate" @@ -388,7 +388,7 @@ class TerminateCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl } /** - * REST handler to adjust a [InteractiveSynchronousCompetitionRun.TaskRun]'s duration. + * REST handler to adjust a [InteractiveSynchronousCompetition.Task]'s duration. */ class AdjustDurationRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/:runId/adjust/:duration" @@ -457,7 +457,7 @@ class ListSubmissionsPerTaskRunAdminHandler : AbstractCompetitionRunAdminRestHan val taskId = ctx.pathParamMap().getOrElse("taskId") { throw ErrorStatusException(404, "Parameter 'taskId' is missing!'", ctx) }.UID() - return run.submissions.filter { it.task()?.taskDescription?.id?.equals(taskId) ?: false }.map { SubmissionInfo.withId(it) } + return run.submissions.filter { it.task?.description?.id?.equals(taskId) ?: false }.map { SubmissionInfo.withId(it) } } } @@ -499,7 +499,7 @@ class OverrideSubmissionStatusRunAdminHandler: AbstractCompetitionRunAdminRestHa } /** - * REST handler to list all viewers for a [InteractiveSynchronousCompetitionRun]. + * REST handler to list all viewers for a [InteractiveSynchronousCompetition]. */ class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), GetRestHandler> { override val route: String = "run/admin/:runId/viewer/list" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt index 31eea593b..f1afd3794 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt @@ -124,8 +124,8 @@ class CompetitionRunClientCurrentTaskInfoHandler : AbstractCompetitionRunClientI return ClientTaskInfo( task.uid.string, - task.taskDescription.name, - task.taskDescription.taskGroup.name, + task.description.name, + task.description.taskGroup.name, when(run.status){ RunManagerStatus.CREATED -> 0 RunManagerStatus.ACTIVE, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index 7ed7fd8a1..8e2086a06 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -21,7 +21,7 @@ import io.javalin.plugin.openapi.annotations.OpenApiResponse /** * A collection of [RestHandler]s that deal with [ScoreOverview]s for ongoing - * [dev.dres.data.model.run.InteractiveSynchronousCompetitionRun]s. + * [dev.dres.data.model.run.InteractiveSynchronousCompetition]s. * * @author Ralph Gasser * @version 1.0.0 @@ -58,7 +58,7 @@ abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler } /** - * Generates and lists all [ScoreOverview]s for the provided [dev.dres.data.model.run.InteractiveSynchronousCompetitionRun]. + * Generates and lists all [ScoreOverview]s for the provided [dev.dres.data.model.run.InteractiveSynchronousCompetition]. * * @author Ralph Gasser * @version 1.0.0 @@ -86,7 +86,7 @@ class ListCompetitionScoreHandler : AbstractScoreRestHandler(), GetRestHandler - task.scorer.scores().map { "${task.taskDescription.name},${task.taskDescription.taskGroup.name},${run.competitionDescription.teams.find { t -> t.uid == it.key }?.name ?: "???"},${it.value}" } + task.scorer.scores().map { "${task.description.name},${task.description.taskGroup.name},${run.competitionDescription.teams.find { t -> t.uid == it.key }?.name ?: "???"},${it.value}" } }.joinToString(separator = "\n") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt index d9d8e4f30..7de4107bd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt @@ -6,8 +6,8 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.dbo.DAO import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.run.SubmissionStatus -import dev.dres.data.model.run.TemporalSubmissionAspect +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.run.RunExecutor import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource @@ -58,7 +58,7 @@ class NextOpenJudgementHandler(val collections: DAO) : Abstract val collection = this.collections[next.second.item.collection] ?: throw ErrorStatusException(404, "Could not find collection with id ${next.second.item.collection}", ctx) - val taskDescription = next.second.task()?.taskDescription?.textualDescription() ?: next.second.task()?.taskDescription?.name ?: "no task description available" + val taskDescription = next.second.task?.description?.textualDescription() ?: next.second.task?.description?.name ?: "no task description available" return if (next.second is TemporalSubmissionAspect){ val tsa = next.second as TemporalSubmissionAspect @@ -206,7 +206,7 @@ class NextOpenVoteJudgementHandler(val collections: DAO) : Abst val collection = this.collections[next.item.collection] ?: throw ErrorStatusException(404, "Could not find collection with id ${next.item.collection}", ctx) - val taskDescription = next.task?.taskDescription?.textualDescription() ?: next.task?.taskDescription?.name ?: "no task description available" + val taskDescription = next.task?.description?.textualDescription() ?: next.task?.description?.name ?: "no task description available" return if (next is TemporalSubmissionAspect){ val tsa = next as TemporalSubmissionAspect diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index 033a86bef..5e0583f39 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -16,6 +16,9 @@ import dev.dres.data.model.basics.media.MediaItemSegmentList import dev.dres.data.model.basics.media.PlayableMediaItem import dev.dres.data.model.competition.TaskType import dev.dres.data.model.run.* +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.run.InteractiveRunManager import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus @@ -91,21 +94,21 @@ class SubmissionHandler (val collections: DAO, private val item map.containsKey(PARAMETER_NAME_SHOT) && item is MediaItem.VideoItem -> { val segmentList = segmentIndex[item.id].firstOrNull() ?: throw ErrorStatusException(400, "Item '${item.name}' not found.", ctx) val time = TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, item, segmentList) ?: throw ErrorStatusException(400, "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", ctx) - TemporalSubmission(team, userId, submissionTime, item, time.first, time.second) + Submission.Temporal(team, userId, submissionTime, item, time.first, time.second) } map.containsKey(PARAMETER_NAME_FRAME) && (item is PlayableMediaItem) -> { val time = TimeUtil.frameToTime(map[PARAMETER_NAME_FRAME]?.first()?.toIntOrNull() ?: throw ErrorStatusException(400, "Parameter '$PARAMETER_NAME_FRAME' must be a number.", ctx), item) val segmentList = segmentIndex[item.id].firstOrNull() ?: throw ErrorStatusException(400, "Item '${item.name}' not found.", ctx) val range = if(mapToSegment && item is MediaItem.VideoItem) (TimeUtil.timeToSegment(time, item, segmentList) ?: throw ErrorStatusException(400, "No segments found for item '${item.name}'.", ctx)) else time to time - TemporalSubmission(team, userId, submissionTime, item, range.first, range.second) + Submission.Temporal(team, userId, submissionTime, item, range.first, range.second) } map.containsKey(PARAMETER_NAME_TIMECODE) && (item is PlayableMediaItem) -> { val time = TimeUtil.timeCodeToMilliseconds(map[PARAMETER_NAME_TIMECODE]?.first()!!, item) ?: throw ErrorStatusException(400, "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", ctx) val segmentList = segmentIndex[item.id].firstOrNull() ?: throw ErrorStatusException(400, "Item '${item.name}' not found.", ctx) val range = if(mapToSegment && item is MediaItem.VideoItem) (TimeUtil.timeToSegment(time, item, segmentList) ?: throw ErrorStatusException(400, "No segments found for item '${item.name}'.", ctx)) else time to time - TemporalSubmission(team, userId, submissionTime, item, range.first, range.second) + Submission.Temporal(team, userId, submissionTime, item, range.first, range.second) } - else -> ItemSubmission(team, userId, submissionTime, item) + else -> Submission.Item(team, userId, submissionTime, item) }.also { it.task = runManager.currentTaskRun } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/RestAuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/RestAuditLogEntry.kt index 62fde063c..e431f0469 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/RestAuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/RestAuditLogEntry.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.types.audit import dev.dres.api.rest.types.AbstractRestEntity import dev.dres.api.rest.types.run.SubmissionInfo import dev.dres.data.model.UID -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.audit.* sealed class RestAuditLogEntry(val type: AuditLogEntryType, id: UID, val timestamp: Long) : AbstractRestEntity(id.string) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt index b8b39f212..46b5b16a4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.run -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.run.RunManager /** - * Contains the basic and most importantly static information about a [InteractiveSynchronousCompetitionRun] and the + * Contains the basic and most importantly static information about a [InteractiveSynchronousCompetition] and the * associated [RunManager]. Since this information usually doesn't change in the course of a run, * it allows for local caching and other optimizations. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt index 8acf0f1ac..968d03696 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt @@ -1,13 +1,13 @@ package dev.dres.api.rest.types.run -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus /** - * Contains the information about the state of a [InteractiveSynchronousCompetitionRun] and the associated [RunManager]. + * Contains the information about the state of a [InteractiveSynchronousCompetition] and the associated [RunManager]. * * This is information that changes in the course of a run an therefore must be updated frequently. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt index ea3839a01..91e023c4a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.run import dev.dres.api.rest.types.collection.RestMediaItem -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus -import dev.dres.data.model.run.TemporalSubmissionAspect +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect /** * Contains information about a [Submission]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt index 9948538bc..6aa454eb5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt @@ -1,11 +1,11 @@ package dev.dres.api.rest.types.run import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.InteractiveSynchronousCompetition /** * Basic and most importantly static information about the [dres.data.model.competition.TaskDescription] - * of a [InteractiveSynchronousCompetitionRun]. Since this information usually doesn't change in the course of a run, it + * of a [InteractiveSynchronousCompetition]. Since this information usually doesn't change in the course of a run, it * allows for local caching and other optimizations. * * @author Ralph Gasser diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt index e61ecebae..a85af0175 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.run import dev.dres.data.model.competition.Team -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.InteractiveSynchronousCompetition /** - * Basic and most importantly static information about the [Team] partaking in a [InteractiveSynchronousCompetitionRun]. + * Basic and most importantly static information about the [Team] partaking in a [InteractiveSynchronousCompetition]. * Since this information usually doesn't change in the course of a run,t allows for local caching * and other optimizations. * diff --git a/backend/src/main/kotlin/dev/dres/data/dbo/DataAccessLayer.kt b/backend/src/main/kotlin/dev/dres/data/dbo/DataAccessLayer.kt index d77c27791..1690d0173 100644 --- a/backend/src/main/kotlin/dev/dres/data/dbo/DataAccessLayer.kt +++ b/backend/src/main/kotlin/dev/dres/data/dbo/DataAccessLayer.kt @@ -1,6 +1,7 @@ package dev.dres.data.dbo -import dev.dres.data.model.run.CompetitionRun +import dev.dres.data.model.run.AbstractRun +import dev.dres.data.model.run.interfaces.Competition import dev.dres.data.serializers.* import java.nio.file.Path @@ -33,8 +34,8 @@ class DataAccessLayer(private val basePath: Path) { /** List of [dev.dres.data.model.competition.CompetitionDescription]s managed by this DRES instance. */ val competitions = DAO(this.basePath.resolve("competitions.db"), competitionSerializer) - /** List of [dev.dres.data.model.run.InteractiveSynchronousCompetitionRun]s managed by this DRES instance. */ - val runs: DAO = DAO(this.basePath.resolve("runs.db"), CompetitionRunSerializer(competitionSerializer)) + /** List of [dev.dres.data.model.run.InteractiveSynchronousCompetition]s managed by this DRES instance. */ + val runs: DAO = DAO(this.basePath.resolve("runs.db"), CompetitionRunSerializer(competitionSerializer)) val audit = DAO(this.basePath.resolve("auditLog.db"), AuditLogEntrySerializer, cacheDuration = 1440) val auditTimes = NumericDaoIndexer(audit){it.timestamp} diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt similarity index 52% rename from backend/src/main/kotlin/dev/dres/data/model/run/Task.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index a40ddde0d..1459475a8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -1,47 +1,33 @@ package dev.dres.data.model.run -import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.TaskDescriptionTarget import dev.dres.data.model.competition.TaskType -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun.TaskRun +import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskRunScorer import dev.dres.run.validation.MediaItemsSubmissionValidator import dev.dres.run.validation.TemporalOverlapSubmissionValidator -import dev.dres.run.validation.interfaces.SubmissionBatchValidator import dev.dres.run.validation.interfaces.SubmissionValidator import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.BasicVoteValidator import dev.dres.run.validation.judged.ItemRange import dev.dres.utilities.TimeUtil +import java.util.* -typealias TaskId = UID +/** + * An abstract [Task] implementation for interactive [Task], i.e. [Task]s that rely on human interaction, such as [Submission]s + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.0 + */ +abstract class AbstractInteractiveTask: AbstractRun(), Task { + /** List of [Submission]s* registered for this [Task]. */ + val submissions: List = LinkedList() -abstract class Task { - - abstract val uid: TaskId - - /** Reference to the [TaskDescription] describing this [Task]. */ - abstract val taskDescription: TaskDescription - - /** The [TaskRunScorer] used to update score for this [Task]. */ - abstract val scorer: TaskRunScorer - -} - -abstract class InteractiveTask : Task() { - - /** Timestamp of when this [TaskRun] was started. */ - @Volatile - var started: Long? = null - internal set - - /** Timestamp of when this [TaskRun] was ended. */ - @Volatile - var ended: Long? = null - internal set + /** The total duration in milliseconds of this task. Usually determined by the [TaskDescription] but can be adjusted! */ + abstract var duration: Long /** The [SubmissionFilter] used to filter [Submission]s. */ abstract val filter: SubmissionFilter @@ -55,13 +41,13 @@ abstract class InteractiveTask : Task() { * * @return [SubmissionValidator]. */ - internal fun newValidator(): SubmissionValidator = when(taskDescription.taskType.targetType.option){ - TaskType.TargetType.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionValidator(setOf((taskDescription.target as TaskDescriptionTarget.MediaItemTarget).item)) - TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionValidator(taskDescription.target as TaskDescriptionTarget.VideoSegmentTarget) - TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionValidator((taskDescription.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) + internal fun newValidator(): SubmissionValidator = when(description.taskType.targetType.option){ + TaskType.TargetType.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionValidator(setOf((description.target as TaskDescriptionTarget.MediaItemTarget).item)) + TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionValidator(description.target as TaskDescriptionTarget.VideoSegmentTarget) + TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionValidator((description.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) TaskType.TargetType.JUDGEMENT -> BasicJudgementValidator(knownCorrectRanges = - (taskDescription.target as TaskDescriptionTarget.JudgementTaskDescriptionTarget).targets.map { - if (it.second == null){ + (description.target as TaskDescriptionTarget.JudgementTaskDescriptionTarget).targets.map { + if (it.second == null) { ItemRange(it.first) } else { val item = it.first @@ -74,8 +60,8 @@ abstract class InteractiveTask : Task() { } }) TaskType.TargetType.VOTE -> BasicVoteValidator( knownCorrectRanges = - (taskDescription.target as TaskDescriptionTarget.VoteTaskDescriptionTarget).targets.map { - if (it.second == null){ + (description.target as TaskDescriptionTarget.VoteTaskDescriptionTarget).targets.map { + if (it.second == null) { ItemRange(it.first) } else { val item = it.first @@ -86,26 +72,10 @@ abstract class InteractiveTask : Task() { } ItemRange(item, range.first, range.second) } }, - parameters = taskDescription.taskType.targetType.parameters + parameters = description.taskType.targetType.parameters ) } abstract fun addSubmission(submission: Submission) -} - -abstract class NonInteractiveTask : Task() { - - @Transient - val validator: SubmissionBatchValidator = newValidator() - - internal fun newValidator(): SubmissionBatchValidator = when(taskDescription.taskType.targetType.option){ - TaskType.TargetType.SINGLE_MEDIA_ITEM -> TODO() - TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TODO() - TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> TODO() - TaskType.TargetType.JUDGEMENT -> TODO() - TaskType.TargetType.VOTE -> TODO() - } - - abstract fun addSubmissionBatch(origin: OriginAspect, batches: List>) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt new file mode 100644 index 000000000..3ab31934e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -0,0 +1,30 @@ +package dev.dres.data.model.run + +import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.aspects.OriginAspect +import dev.dres.data.model.submissions.batch.ResultBatch +import dev.dres.run.validation.interfaces.SubmissionBatchValidator + +/** + * An abstract [Task] implementation for non-interactive [Task], i.e., [Task]s that do not rely on human interaction and simply process input data in batches + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.0 + */ +abstract class AbstractNonInteractiveTask: AbstractRun(), Task { + + @Transient + val validator: SubmissionBatchValidator = newValidator() + + internal fun newValidator(): SubmissionBatchValidator = when(this.description.taskType.targetType.option){ + TaskType.TargetType.SINGLE_MEDIA_ITEM -> TODO() + TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TODO() + TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> TODO() + TaskType.TargetType.JUDGEMENT -> TODO() + TaskType.TargetType.VOTE -> TODO() + } + + abstract fun addSubmissionBatch(origin: OriginAspect, batches: List>) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractRun.kt new file mode 100644 index 000000000..e14c8e318 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractRun.kt @@ -0,0 +1,43 @@ +package dev.dres.data.model.run + + +import dev.dres.data.model.run.interfaces.Run + +/** + * An abstract [Run] implementation that can be used by different subtypes. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +abstract class AbstractRun: Run { + + /** Timestamp of when this [AbstractRun] was started. */ + @Volatile + override var started: Long? = null + protected set + + /** Timestamp of when this [AbstractRun] was ended. */ + @Volatile + override var ended: Long? = null + protected set + + /** + * Starts this [AbstractRun]. + */ + override fun start() { + if (this.hasStarted) { + throw IllegalStateException("Run has already been started.") + } + this.started = System.currentTimeMillis() + } + + /** + * Ends this [AbstractRun]. + */ + override fun end() { + if (!this.isRunning) { + this.started = System.currentTimeMillis() + } + this.ended = System.currentTimeMillis() + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/CompetitionRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/CompetitionRun.kt deleted file mode 100644 index 67a33c80a..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/CompetitionRun.kt +++ /dev/null @@ -1,45 +0,0 @@ -package dev.dres.data.model.run - -import dev.dres.data.model.Entity -import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun.TaskRun - -typealias CompetitionRunId = UID - - -abstract class CompetitionRun(override var id: CompetitionRunId, val name: String, val competitionDescription: CompetitionDescription): Run, Entity { - - /** Timestamp of when this [InteractiveSynchronousCompetitionRun] was started. */ - @Volatile - override var started: Long? = null - protected set - - /** Timestamp of when this [TaskRun] was ended. */ - @Volatile - override var ended: Long? = null - protected set - - /** - * Starts this [InteractiveSynchronousCompetitionRun]. - */ - open fun start() { - if (this.hasStarted) { - throw IllegalStateException("Competition run '$name' has already been started.") - } - this.started = System.currentTimeMillis() - } - - /** - * Ends this [InteractiveSynchronousCompetitionRun]. - */ - open fun end() { - if (!this.isRunning) { - this.started = System.currentTimeMillis() - } - this.ended = System.currentTimeMillis() - } - - abstract val tasks: List - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt new file mode 100644 index 000000000..718f60456 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -0,0 +1,112 @@ +package dev.dres.data.model.run + +import dev.dres.data.model.UID +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.TaskDescriptionId +import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.run.interfaces.Competition +import dev.dres.data.model.run.interfaces.CompetitionId +import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.submissions.Submission +import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.validation.interfaces.SubmissionValidator +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +/** + * Represents a concrete, interactive and asynchronous [Run] of a [CompetitionDescription]. + * + * [InteractiveAsynchronousCompetition]s can be started and ended and they can be used to create new [Task]s and access the current [Task]. + * + * @author Ralph Gasser + * @param 1.0.0 + */ +class InteractiveAsynchronousCompetition(override var id: CompetitionId, override val name: String, override val description: CompetitionDescription): AbstractRun(), Competition { + + init { + require(description.tasks.size > 0) { "Cannot create a run from a competition that doesn't have any tasks. "} + require(description.teams.size > 0) { "Cannot create a run from a competition that doesn't have any teams. "} + } + + /** A [ConcurrentHashMap] that maps a list of [Task]s to the [TeamId]s they belong to.*/ + private val tasksMap = ConcurrentHashMap>() + + /** A [List] of all active [Task]s.*/ + override val tasks: List + get() = this.tasksMap.values.flatten() + + /** + * Generates and returns a [String] representation for this [InteractiveAsynchronousCompetition]. + */ + override fun toString(): String = "InteractiveAsynchronousCompetition(id=$id, name=${name})" + + /** + * A [AbstractInteractiveTask] that takes place as part of the [InteractiveAsynchronousCompetition]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ + inner class Task internal constructor (override val uid: TaskId = UID(), val teamId: TeamId, val taskDescriptionId: TaskDescriptionId): AbstractInteractiveTask() { + + internal constructor(uid: TaskId, teamId: TeamId, taskId: TaskDescriptionId, started: Long, ended: Long): this(uid, teamId, taskId) { + this.started = if (started == -1L) { null } else { started } + this.ended = if (ended == -1L) { null } else { ended } + } + + /** The [InteractiveAsynchronousCompetition] this [Task] belongs to.*/ + override val competition: InteractiveAsynchronousCompetition + get() = this@InteractiveAsynchronousCompetition + + /** The position of this [Task] within the [InteractiveAsynchronousCompetition]. */ + override val position: Int + get() = this@InteractiveAsynchronousCompetition.tasksMap[this.teamId]?.indexOf(this) ?: -1 + + @Transient + override val description: TaskDescription = this@InteractiveAsynchronousCompetition.description.tasks.find { it.id == this.taskDescriptionId } + ?: throw IllegalArgumentException("Task with taskId ${this.taskDescriptionId} not found.") + + @Transient + override val filter: SubmissionFilter = this.description.newFilter() + + @Transient + override val scorer: TaskRunScorer = this.description.newScorer() + + @Transient + override val validator: SubmissionValidator = this.newValidator() + + /** The total duration in milliseconds of this task. Usually determined by the [TaskDescription] but can be adjusted! */ + override var duration: Long = this.description.duration + + init { + check(this@InteractiveAsynchronousCompetition.description.teams.any { it.uid == this.teamId }) { + "Cannot start a new task run for team with ID ${this.teamId}. Team is not registered for competition." + } + this@InteractiveAsynchronousCompetition.tasksMap.compute(this.teamId) { _, v -> + val list = v ?: LinkedList() + check(list.isEmpty() || list.last().hasEnded) { "Cannot create a new task. Another task is currently running." } + list.add(this) + list + } + } + + /** + * Adds a [Submission] to this [InteractiveAsynchronousCompetition.Task]. + * + * @param submission The [Submission] to add. + * @throws IllegalArgumentException If [Submission] could not be added for any reason. + */ + @Synchronized + override fun addSubmission(submission: Submission) { + check(!this.isRunning) { "Task run '${this@InteractiveAsynchronousCompetition.name}.${this.position}' is currently not running." } + check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task." } + check(this.filter.test(submission)) { "The provided submission $submission was rejected by the filter." } + + /* Process Submission. */ + (this.submissions as MutableList).add(submission) + this.validator.validate(submission) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetitionRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetitionRun.kt deleted file mode 100644 index fdd105c67..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetitionRun.kt +++ /dev/null @@ -1,54 +0,0 @@ -package dev.dres.data.model.run - -import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.competition.TaskDescriptionId -import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun.TaskRun -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskRunScorer -import dev.dres.run.validation.interfaces.SubmissionValidator - -class InteractiveAsynchronousCompetitionRun(override var id: CompetitionRunId, name: String, competitionDescription: CompetitionDescription): CompetitionRun(id, name, competitionDescription) { - - - override val tasks: List - get() = TODO("Not yet implemented") - - - - inner class TeamTaskRun (override val uid: TaskId = UID(), val teamId: TeamId, val taskDescriptionId: TaskDescriptionId): Run, InteractiveTask() { - - internal constructor(uid: TaskId, teamId: TeamId, taskId: TaskDescriptionId, started: Long, ended: Long): this(uid, teamId, taskId) { - this.started = if (started == -1L) { null } else { started } - this.ended = if (ended == -1L) { null } else { ended } - } - - /** List of [Submission]s* registered for this [TaskRun]. */ - val submissions: List = mutableListOf() - - - - override fun addSubmission(submission: Submission) { - TODO("Not yet implemented") - } - - override val taskDescription: TaskDescription - get() = this@InteractiveAsynchronousCompetitionRun.competitionDescription - .tasks.find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") - - @Transient - override val filter: SubmissionFilter = taskDescription.newFilter() - - @Transient - override val scorer: TaskRunScorer = taskDescription.newScorer() - - @Transient - override val validator: SubmissionValidator = newValidator() - - - } - - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt new file mode 100644 index 000000000..d1092affa --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt @@ -0,0 +1,113 @@ +package dev.dres.data.model.run + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import dev.dres.data.model.UID +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.TaskDescriptionId +import dev.dres.data.model.run.InteractiveSynchronousCompetition.Task +import dev.dres.data.model.run.interfaces.Competition +import dev.dres.data.model.run.interfaces.CompetitionId +import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.submissions.Submission +import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.validation.interfaces.SubmissionValidator +import java.util.* + + +/** + * Represents a concrete, interactive and synchronous [Run] of a [CompetitionDescription]. + * + * [InteractiveSynchronousCompetition]s can be started and ended and they can be used to create new [Task]s and access the current [Task]. + * + * @author Ralph Gasser + * @param 1.3.0 + */ +class InteractiveSynchronousCompetition(override var id: CompetitionId, override val name: String, override val description: CompetitionDescription): AbstractRun(), Competition { + + internal constructor(id: CompetitionId, name: String, competitionDescription: CompetitionDescription, started: Long, ended: Long) : this(id, name, competitionDescription) { + this.started = if (started == -1L) { null } else { started } + this.ended = if (ended == -1L) { null } else { ended } + } + + init { + require(this.description.tasks.size > 0) { "Cannot create a run from a competition that doesn't have any tasks. "} + require(this.description.teams.size > 0) { "Cannot create a run from a competition that doesn't have any teams. "} + } + + /** List of [Task]s registered for this [InteractiveSynchronousCompetition]. */ + override val tasks: List = LinkedList() + + /** Returns the last [Task]. */ + val lastTask: Task? + get() = this.tasks.lastOrNull() + + override fun toString(): String = "InteractiveSynchronousCompetition(id=$id, name=${name})" + + /** + * Represents a concrete [Run] of a [TaskDescription]. [Task]s always exist within a [InteractiveSynchronousCompetition]. + * As a [InteractiveSynchronousCompetition], [Task]s can be started and ended and they can be used to register [Submission]s. + * + * @version 1.2.0 + * @author Ralph Gasser + */ + @JsonIgnoreProperties(value = ["competition"]) + inner class Task(override val uid: TaskId = UID(), val taskDescriptionId: TaskDescriptionId): Run, AbstractInteractiveTask() { + + internal constructor(uid: TaskId, taskId: TaskDescriptionId, started: Long, ended: Long): this(uid, taskId) { + this.started = if (started == -1L) { null } else { started } + this.ended = if (ended == -1L) { null } else { ended } + } + + /** The [InteractiveSynchronousCompetition] this [Task] belongs to.*/ + override val competition: InteractiveSynchronousCompetition + get() = this@InteractiveSynchronousCompetition + + /** The position of this [Task] within the [InteractiveSynchronousCompetition]. */ + override val position: Int + get() = this@InteractiveSynchronousCompetition.tasks.indexOf(this) + + /** Reference to the [TaskDescription] describing this [Task]. */ + @Transient + override val description: TaskDescription = this@InteractiveSynchronousCompetition.description.tasks.find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") + + @Transient + override val filter: SubmissionFilter = description.newFilter() + + @Transient + override val scorer: TaskRunScorer = description.newScorer() + + @Transient + override val validator: SubmissionValidator = newValidator() + + /** The total duration in milliseconds of this task. Usually determined by the [TaskDescription] but can be adjusted! */ + override var duration: Long = this.description.duration + + init { + check(this@InteractiveSynchronousCompetition.tasks.isEmpty() || this@InteractiveSynchronousCompetition.tasks.last().hasEnded) { + "Cannot create a new task. Another task is currently running." + } + (this@InteractiveSynchronousCompetition.tasks as MutableList).add(this) + } + + /** + * Adds a [Submission] to this [Task]. + * + * @param submission The [Submission] to add. + */ + @Synchronized + override fun addSubmission(submission: Submission) { + check(this.isRunning) { "Task run '${this@InteractiveSynchronousCompetition.name}.${this.position}' is currently not running." } + check(this@InteractiveSynchronousCompetition.description.teams.any { it.uid == submission.teamId }) { + "Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousCompetition.name}." + } + check(this.filter.test(submission)) { "The provided submission $submission was rejected by the filter." } + + /* Process Submission. */ + (this.submissions as MutableList).add(submission) + this.validator.validate(submission) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetitionRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetitionRun.kt deleted file mode 100644 index 07420335b..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetitionRun.kt +++ /dev/null @@ -1,149 +0,0 @@ -package dev.dres.data.model.run - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties -import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.competition.TaskDescriptionId -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun.TaskRun -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskRunScorer -import dev.dres.run.validation.interfaces.SubmissionValidator -import java.util.* - - -/** - * Represents a concrete [Run] of a [CompetitionDescription]. [InteractiveSynchronousCompetitionRun]s can be started and - * ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. - * - * @author Ralph Gasser - * @param 1.2.1 - */ -class InteractiveSynchronousCompetitionRun(override var id: CompetitionRunId, name: String, competitionDescription: CompetitionDescription): CompetitionRun(id, name, competitionDescription) { - - internal constructor(id: CompetitionRunId, name: String, competitionDescription: CompetitionDescription, started: Long, ended: Long) : this(id, name, competitionDescription) { - this.started = if (started == -1L) { null } else { started } - this.ended = if (ended == -1L) { null } else { ended } - } - - init { - require(competitionDescription.tasks.size > 0) { "Cannot create a run from a competition that doesn't have any tasks. "} - require(competitionDescription.teams.size > 0) { "Cannot create a run from a competition that doesn't have any teams. "} - } - - /** List of [TaskRun]s registered for this [InteractiveSynchronousCompetitionRun]. */ - override val tasks: List = LinkedList() - - /** Returns the last [TaskRun]. */ - val lastTask: TaskRun? - get() = this.tasks.lastOrNull() - - /** - * Creates a new [TaskRun] for the given [TaskDescription]. - * - * @param taskId [UID] of the [TaskDescription] to start a [TaskRun] for. - */ - fun newTaskRun(taskId: TaskDescriptionId): TaskRun { - if (this@InteractiveSynchronousCompetitionRun.tasks.isEmpty() || this@InteractiveSynchronousCompetitionRun.tasks.last().hasEnded) { - val ret = TaskRun(taskDescriptionId = taskId) - (this.tasks as MutableList).add(ret) - return ret - } else { - throw IllegalStateException("Another Task is currently running.") - } - } - - override fun toString(): String = "InteractiveCompetitionRun(id=$id, name=${name})" - - - /** - * Represents a concrete [Run] of a [TaskDescription]. [TaskRun]s always exist within a [InteractiveSynchronousCompetitionRun]. - * As a [InteractiveSynchronousCompetitionRun], [TaskRun]s can be started and ended and they can be used to register [Submission]s. - * - * @version 1.2.0 - * @author Ralph Gasser - */ - @JsonIgnoreProperties(value = ["competition"]) - inner class TaskRun (override val uid: TaskId = UID(), val taskDescriptionId: TaskDescriptionId): Run, InteractiveTask() { - - internal constructor(uid: TaskId, taskId: TaskDescriptionId, started: Long, ended: Long): this(uid, taskId) { - this.started = if (started == -1L) { null } else { started } - this.ended = if (ended == -1L) { null } else { ended } - } - - /** List of [Submission]s* registered for this [TaskRun]. */ - val submissions: List = mutableListOf() - - /** The [InteractiveSynchronousCompetitionRun] this [TaskRun] belongs to.*/ - val competition: InteractiveSynchronousCompetitionRun - get() = this@InteractiveSynchronousCompetitionRun - - /** The position of this [TaskRun] within the [InteractiveSynchronousCompetitionRun]. */ - val position: Int - get() = this@InteractiveSynchronousCompetitionRun.tasks.indexOf(this) - - /** Reference to the [TaskDescription] describing this [TaskRun]. */ - @Transient - override val taskDescription: TaskDescription = this@InteractiveSynchronousCompetitionRun.competitionDescription.tasks.find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") - - - @Transient - override val filter: SubmissionFilter = taskDescription.newFilter() - - @Transient - override val scorer: TaskRunScorer = taskDescription.newScorer() - - @Transient - override val validator: SubmissionValidator = newValidator() - - - /** Duration of this [TaskRun]. Defaults to the duration specified in the [TaskDescription]. */ - @Volatile - var duration: Long = this.taskDescription.duration - - /** - * Starts this [InteractiveSynchronousCompetitionRun.TaskRun]. - */ - internal fun start() { - if (this.hasStarted) { - throw IllegalStateException("Task run '${this@InteractiveSynchronousCompetitionRun.name}.${this.position}' has already been started.") - } - this.started = System.currentTimeMillis() - } - - /** - * Ends this [InteractiveSynchronousCompetitionRun.TaskRun]. - */ - internal fun end() { - if (!this.isRunning) { - val end = System.currentTimeMillis() - this.started = end - this.ended = end - } else { - this.ended = System.currentTimeMillis() - } - } - - /** - * Adds a [Submission] to this [TaskRun]. - * - * @param submission The [Submission] to add. - */ - @Synchronized - override fun addSubmission(submission: Submission) { - if (!this.isRunning) { - throw IllegalStateException("Task run '${this@InteractiveSynchronousCompetitionRun.name}.${this.position}' is currently not running.") - } - if (!this@InteractiveSynchronousCompetitionRun.competitionDescription.teams.any { it.uid == submission.teamId }) { - throw IllegalStateException("Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousCompetitionRun.name}.") - } - if (!this.filter.test(submission)) { - throw IllegalArgumentException("The provided submission $submission was rejected.") - } - - /* Process Submission. */ - (this.submissions as MutableList).add(submission) - this.validator.validate(submission) - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt new file mode 100644 index 000000000..8f491dda1 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt @@ -0,0 +1,47 @@ +package dev.dres.data.model.run + +import dev.dres.data.model.UID +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.TaskDescriptionId +import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.run.interfaces.Competition +import dev.dres.data.model.run.interfaces.CompetitionId +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.submissions.aspects.OriginAspect +import dev.dres.data.model.submissions.batch.ResultBatch +import dev.dres.run.score.interfaces.TaskRunScorer + + +class NonInteractiveCompetition(override var id: CompetitionId, override val name: String, override val description: CompetitionDescription): AbstractRun(), Competition { + + /** */ + override val tasks: List = this.description.tasks.map { TaskContainer(taskDescriptionId = it.id) } + + + inner class TaskContainer(override val uid: TaskId = UID(), val taskDescriptionId: TaskDescriptionId) : AbstractNonInteractiveTask() { + + internal val submissions : MutableMap, ResultBatch<*>> = mutableMapOf() + + @Synchronized + override fun addSubmissionBatch(origin: OriginAspect, batches: List>) { + + batches.forEach { resultBatch -> + submissions[origin.teamId to resultBatch.name] = resultBatch + } + } + + + override val competition: Competition + get() = this@NonInteractiveCompetition + + override val position: Int + get() = this@NonInteractiveCompetition.tasks.indexOf(this) + + override val description: TaskDescription = this@NonInteractiveCompetition.description.tasks + .find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") + + @Transient + override val scorer: TaskRunScorer = description.newScorer() + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetitonRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetitonRun.kt deleted file mode 100644 index 25d021678..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetitonRun.kt +++ /dev/null @@ -1,40 +0,0 @@ -package dev.dres.data.model.run - -import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.competition.TaskDescriptionId -import dev.dres.data.model.competition.TeamId -import dev.dres.run.score.interfaces.TaskRunScorer - - -open class NonInteractiveCompetitionRun(override var id: CompetitionRunId, name: String, competitionDescription: CompetitionDescription): CompetitionRun(id, name, competitionDescription) { - - override val tasks: List = competitionDescription.tasks.map { TaskContainer(taskDescriptionId = it.id) } - - - inner class TaskContainer(override val uid: TaskId = UID(), val taskDescriptionId: TaskDescriptionId) : NonInteractiveTask() { - - internal val submissions : MutableMap, ResultBatch<*>> = mutableMapOf() - - @Synchronized - override fun addSubmissionBatch(origin: OriginAspect, batches: List>) { - - batches.forEach { resultBatch -> - submissions[origin.teamId to resultBatch.name] = resultBatch - } - - } - - override val taskDescription: TaskDescription = this@NonInteractiveCompetitionRun.competitionDescription.tasks - .find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") - - @Transient - override val scorer: TaskRunScorer = taskDescription.newScorer() - - } - - - - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 5556fade4..da5bb62a7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -10,11 +10,7 @@ import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context -data class RunActionContext( - val userId: UserId, - val teamId: TeamId?, - val roles: Set -) { +data class RunActionContext(val userId: UserId, val teamId: TeamId?, val roles: Set) { val isAdmin: Boolean get() = roles.contains(Role.ADMIN) @@ -28,7 +24,5 @@ data class RunActionContext( } val DUMMY_ADMIN = RunActionContext(UID.EMPTY, UID.EMPTY, setOf(Role.ADMIN)) - } - } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Submission.kt deleted file mode 100644 index 75a15b8a0..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Submission.kt +++ /dev/null @@ -1,141 +0,0 @@ -package dev.dres.data.model.run - -import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.time.TemporalPoint -import dev.dres.data.model.basics.time.TemporalRange -import dev.dres.data.model.basics.time.TemporalUnit - -/** - * A [Submission] as received by a competition participant. - * - * @author Ralph Gasser & Luca Rossetto - * @version 1.0.1 - */ - -interface StatusAspect { - var status: SubmissionStatus -} - -interface ItemAspect { - val item: MediaItem -} - -interface OriginAspect { - val uid: UID - val teamId: UID - val memberId: UID -} - -interface BaseSubmissionAspect : StatusAspect, ItemAspect, OriginAspect { - val timestamp: Long - fun task(): Task? -} - -interface TemporalAspect { - - /** Start time in milliseconds */ - val start: Long - - /** End time in milliseconds */ - val end: Long - - val temporalRange: TemporalRange -} - -interface TemporalSubmissionAspect : BaseSubmissionAspect, TemporalAspect - -interface SpatialSubmissionAspect : BaseSubmissionAspect { - //TODO some spatial representation -} - -sealed class Submission(override val teamId: UID, - override val memberId: UID, - override val timestamp: Long, - override val item: MediaItem, - override val uid: UID -) : BaseSubmissionAspect { - - override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE - - @Transient - @JsonIgnore - internal var task: InteractiveSynchronousCompetitionRun.TaskRun? = null - - override fun task(): InteractiveSynchronousCompetitionRun.TaskRun? = task - - -} - -data class ItemSubmission(override val teamId: UID, - override val memberId: UID, - override val timestamp: Long, - override val item: MediaItem, - override val uid: UID = UID() -) : Submission(teamId, memberId, timestamp, item, uid) - -data class TemporalSubmission(override val teamId: UID, - override val memberId: UID, - override val timestamp: Long, - override val item: MediaItem, - override val start: Long, //in ms - override val end: Long, //in ms - override val uid: UID = UID() -) : Submission(teamId, memberId, timestamp, item, uid), TemporalSubmissionAspect { - - override val temporalRange: TemporalRange - get() = TemporalRange(TemporalPoint(start.toDouble(), TemporalUnit.MILLISECONDS), TemporalPoint(end.toDouble(), TemporalUnit.MILLISECONDS)) - -} - -/******************************************************************/ -interface BaseResultBatchElement : ItemAspect, StatusAspect - -data class ItemBatchElement(override val item: MediaItem): BaseResultBatchElement { - override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE -} - -data class TemporalBatchElement( - override val item: MediaItem, - override val start: Long, - override val end: Long, -) : BaseResultBatchElement, TemporalAspect { - override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE - override val temporalRange: TemporalRange - get() = TemporalRange(TemporalPoint(start.toDouble(), TemporalUnit.MILLISECONDS), TemporalPoint(end.toDouble(), TemporalUnit.MILLISECONDS)) -} - -/******************************************************************/ - -interface ResultBatch { - val task: TaskId - val name: String - val results: List -} - -data class BaseResultBatch( - override val task: TaskId, - override val name: String, - override val results: List -) : ResultBatch - -/******************************************************************/ - -interface SubmissionBatch> : OriginAspect { - val results : Collection -} - -data class BaseSubmissionBatch( - override val uid: UID, - override val teamId: UID, - override val memberId: UID, - override val results: Collection> -) : SubmissionBatch> - -data class TemporalSubmissionBatch( - override val teamId: UID, - override val memberId: UID, - override val uid: UID, - override val results: List>, -) : SubmissionBatch> \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt new file mode 100644 index 000000000..ffecc9699 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt @@ -0,0 +1,24 @@ +package dev.dres.data.model.run.interfaces + +import dev.dres.data.model.Entity +import dev.dres.data.model.competition.CompetitionDescription + +/** + * Represents a [Competition] that a DRES user or client takes place in and that groups several [Task]s + * + * @author Ralph Gasser + * @version 1.0.0 + */ +interface Competition: Run, Entity { + /** The unique [CompetitionId] that identifies this [Competition]. Used by the persistence layer. */ + override var id: CompetitionId + + /** The name human readable of this [Competition]. */ + val name: String + + /** Reference to the [CompetitionDescription] that describes the content of this [Competition]. */ + val description: CompetitionDescription + + /** Collection of [Task]s that make up this [Competition]. */ + val tasks: List +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt new file mode 100644 index 000000000..b92e5ba5a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt @@ -0,0 +1,10 @@ +package dev.dres.data.model.run.interfaces + +import dev.dres.data.model.UID + +/** The ID of a [Task]. */ +typealias TaskId = UID + + +/** The ID of a [Competition]. */ +typealias CompetitionId = UID \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Run.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Run.kt similarity index 66% rename from backend/src/main/kotlin/dev/dres/data/model/run/Run.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Run.kt index edf16c222..5941ef835 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Run.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Run.kt @@ -1,10 +1,12 @@ -package dev.dres.data.model.run +package dev.dres.data.model.run.interfaces + +import java.rmi.server.UID /** * A [Run] that can be started and ended and keeps track of the points in time, these events took place. * * @author Ralph Gasser - * @version 1.0 + * @version 1.0.0 */ interface Run { /** Timestamp of when this [Run] was started. */ @@ -24,6 +26,20 @@ interface Run { /** Boolean indicating whether this [Run] has ended. */ val hasEnded: Boolean get() = this.ended != null + + /** + * Starts this [Run]. + * + * @throws IllegalStateException If [Run] is already running or has ended. + */ + fun start() + + /** + * Ends this [Run]. + * + * @throws IllegalStateException If [Run] has ended running. + */ + fun end() } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt new file mode 100644 index 000000000..d81492ada --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt @@ -0,0 +1,29 @@ +package dev.dres.data.model.run.interfaces + +import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task +import dev.dres.data.model.submissions.Submission +import dev.dres.run.score.interfaces.TaskRunScorer + +/** + * Represents a [Task] solved by a DRES user or client. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +interface Task: Run { + /** The unique [TaskId] that identifies this [Task]. Used by the persistence layer. */ + val uid: TaskId + + /** Reference to the [Competition] this [Task] belongs to. */ + val competition: Competition + + /** The position of this [Task] within the enclosing [Competition]. */ + val position: Int + + /** Reference to the [TaskDescription] describing this [Task]. */ + val description: TaskDescription + + /** The [TaskRunScorer] used to update score for this [Task]. */ + val scorer: TaskRunScorer +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt new file mode 100644 index 000000000..04dec6a1d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -0,0 +1,58 @@ +package dev.dres.data.model.submissions + +import com.fasterxml.jackson.annotation.JsonIgnore +import dev.dres.data.model.UID +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.basics.time.TemporalPoint +import dev.dres.data.model.basics.time.TemporalRange +import dev.dres.data.model.basics.time.TemporalUnit +import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.BaseSubmissionAspect +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect + + +/** + * A [Submission] as received by a competition participant. + * + * @author Ralph Gasser & Luca Rossetto + * @version 1.1.0 + */ +sealed class Submission : BaseSubmissionAspect { + + /** The [AbstractInteractiveTask] this [Submission] belongs to. */ + override var task: AbstractInteractiveTask? = null + + /** The [SubmissionStatus] of this [Submission]. */ + override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE + + /** + * + */ + data class Item( + override val teamId: UID, + override val memberId: UID, + override val timestamp: Long, + override val item: MediaItem, + override val uid: UID = UID() + ) : Submission() + + /** + * + */ + data class Temporal( + override val teamId: UID, + override val memberId: UID, + override val timestamp: Long, + override val item: MediaItem, + override val start: Long, //in ms + override val end: Long, //in ms + override val uid: UID = UID() + ) : Submission(), TemporalSubmissionAspect { + + override val temporalRange: TemporalRange + get() = TemporalRange(TemporalPoint(start.toDouble(), TemporalUnit.MILLISECONDS), TemporalPoint(end.toDouble(), TemporalUnit.MILLISECONDS)) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/SubmissionStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt similarity index 89% rename from backend/src/main/kotlin/dev/dres/data/model/run/SubmissionStatus.kt rename to backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt index 43373b82c..4bd6b622e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/SubmissionStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.run +package dev.dres.data.model.submissions /** * Status of a [Submission] with respect to its validation. diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/BaseSubmissionAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/BaseSubmissionAspect.kt new file mode 100644 index 000000000..f6bad88d5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/BaseSubmissionAspect.kt @@ -0,0 +1,13 @@ +package dev.dres.data.model.submissions.aspects + +import dev.dres.data.model.run.AbstractInteractiveTask + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface BaseSubmissionAspect : StatusAspect, ItemAspect, OriginAspect { + var task: AbstractInteractiveTask? + val timestamp: Long +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt new file mode 100644 index 000000000..d1a792b8f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt @@ -0,0 +1,12 @@ +package dev.dres.data.model.submissions.aspects + +import dev.dres.data.model.basics.media.MediaItem + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface ItemAspect { + val item: MediaItem +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt new file mode 100644 index 000000000..76fb132e6 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt @@ -0,0 +1,14 @@ +package dev.dres.data.model.submissions.aspects + +import dev.dres.data.model.UID + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface OriginAspect { + val uid: UID + val teamId: UID + val memberId: UID +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/SpatialSubmissionAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/SpatialSubmissionAspect.kt new file mode 100644 index 000000000..699fe78a3 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/SpatialSubmissionAspect.kt @@ -0,0 +1,8 @@ +package dev.dres.data.model.submissions.aspects + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface SpatialSubmissionAspect : BaseSubmissionAspect \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/StatusAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/StatusAspect.kt new file mode 100644 index 000000000..c29b50572 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/StatusAspect.kt @@ -0,0 +1,12 @@ +package dev.dres.data.model.submissions.aspects + +import dev.dres.data.model.submissions.SubmissionStatus + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface StatusAspect { + var status: SubmissionStatus +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt new file mode 100644 index 000000000..f7740cb46 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt @@ -0,0 +1,19 @@ +package dev.dres.data.model.submissions.aspects + +import dev.dres.data.model.basics.time.TemporalRange + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface TemporalAspect { + + /** Start time in milliseconds */ + val start: Long + + /** End time in milliseconds */ + val end: Long + + val temporalRange: TemporalRange +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalSubmissionAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalSubmissionAspect.kt new file mode 100644 index 000000000..f5fc5d8a1 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalSubmissionAspect.kt @@ -0,0 +1,8 @@ +package dev.dres.data.model.submissions.aspects + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface TemporalSubmissionAspect : BaseSubmissionAspect, TemporalAspect \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt new file mode 100644 index 000000000..8df5e68af --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt @@ -0,0 +1,14 @@ +package dev.dres.data.model.submissions.batch + +import dev.dres.data.model.run.interfaces.TaskId + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +data class BaseResultBatch( + override val task: TaskId, + override val name: String, + override val results: List +) : ResultBatch \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatchElement.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatchElement.kt new file mode 100644 index 000000000..86538b0cb --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatchElement.kt @@ -0,0 +1,11 @@ +package dev.dres.data.model.submissions.batch + +import dev.dres.data.model.submissions.aspects.ItemAspect +import dev.dres.data.model.submissions.aspects.StatusAspect + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface BaseResultBatchElement : ItemAspect, StatusAspect diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt new file mode 100644 index 000000000..abacb028f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt @@ -0,0 +1,15 @@ +package dev.dres.data.model.submissions.batch + +import dev.dres.data.model.UID + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +data class BaseSubmissionBatch( + override val uid: UID, + override val teamId: UID, + override val memberId: UID, + override val results: Collection> +) : SubmissionBatch> diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt new file mode 100644 index 000000000..cc17e396d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt @@ -0,0 +1,13 @@ +package dev.dres.data.model.submissions.batch + +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.submissions.SubmissionStatus + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +data class ItemBatchElement(override val item: MediaItem): BaseResultBatchElement { + override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt new file mode 100644 index 000000000..287edc090 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt @@ -0,0 +1,14 @@ +package dev.dres.data.model.submissions.batch + +import dev.dres.data.model.run.interfaces.TaskId + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +interface ResultBatch { + val task: TaskId + val name: String + val results: List +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/SubmissionBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/SubmissionBatch.kt new file mode 100644 index 000000000..3428adeb1 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/SubmissionBatch.kt @@ -0,0 +1,12 @@ +package dev.dres.data.model.submissions.batch + +import dev.dres.data.model.submissions.aspects.OriginAspect + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +interface SubmissionBatch> : OriginAspect { + val results : Collection +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt new file mode 100644 index 000000000..f8a00f1ad --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt @@ -0,0 +1,19 @@ +package dev.dres.data.model.submissions.batch + +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.basics.time.TemporalPoint +import dev.dres.data.model.basics.time.TemporalRange +import dev.dres.data.model.basics.time.TemporalUnit +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.TemporalAspect + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +data class TemporalBatchElement(override val item: MediaItem, override val start: Long, override val end: Long, ) : BaseResultBatchElement, TemporalAspect { + override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE + override val temporalRange: TemporalRange + get() = TemporalRange(TemporalPoint(start.toDouble(), TemporalUnit.MILLISECONDS), TemporalPoint(end.toDouble(), TemporalUnit.MILLISECONDS)) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt new file mode 100644 index 000000000..7d5420cc5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt @@ -0,0 +1,15 @@ +package dev.dres.data.model.submissions.batch + +import dev.dres.data.model.UID + +/** + * + * @author Luca Rossetto + * @version 1.0.0 + */ +data class TemporalSubmissionBatch( + override val teamId: UID, + override val memberId: UID, + override val uid: UID, + override val results: List>, +) : SubmissionBatch> \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/AuditLogEntrySerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/AuditLogEntrySerializer.kt index 0e5f8c41c..98258b410 100644 --- a/backend/src/main/kotlin/dev/dres/data/serializers/AuditLogEntrySerializer.kt +++ b/backend/src/main/kotlin/dev/dres/data/serializers/AuditLogEntrySerializer.kt @@ -1,6 +1,6 @@ package dev.dres.data.serializers -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.audit.* import dev.dres.utilities.extensions.readUID import dev.dres.utilities.extensions.writeUID diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionRunSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionRunSerializer.kt index c1fa64563..b98b84317 100644 --- a/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionRunSerializer.kt +++ b/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionRunSerializer.kt @@ -1,9 +1,9 @@ package dev.dres.data.serializers -import dev.dres.data.model.run.CompetitionRun -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun -import dev.dres.data.model.run.NonInteractiveCompetitionRun -import dev.dres.data.model.run.Submission +import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.NonInteractiveCompetition +import dev.dres.data.model.run.interfaces.Competition +import dev.dres.data.model.submissions.Submission import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.readUID import dev.dres.utilities.extensions.writeUID @@ -11,21 +11,21 @@ import org.mapdb.DataInput2 import org.mapdb.DataOutput2 import org.mapdb.Serializer -class CompetitionRunSerializer(private val competitionSerializer: CompetitionSerializer): Serializer { - override fun serialize(out: DataOutput2, value: CompetitionRun) { +class CompetitionRunSerializer(private val competitionSerializer: CompetitionSerializer): Serializer { + override fun serialize(out: DataOutput2, value: Competition) { when(value) { - is InteractiveSynchronousCompetitionRun -> out.packInt(1) - is NonInteractiveCompetitionRun -> out.packInt(2) + is InteractiveSynchronousCompetition -> out.packInt(1) + is NonInteractiveCompetition -> out.packInt(2) } out.writeUID(value.id) out.writeUTF(value.name) - competitionSerializer.serialize(out, value.competitionDescription) + this.competitionSerializer.serialize(out, value.description) out.writeLong(value.started ?: -1) out.writeLong(value.ended ?: -1) out.writeInt(value.tasks.size) when(value){ - is InteractiveSynchronousCompetitionRun -> { + is InteractiveSynchronousCompetition -> { for (taskRun in value.tasks) { out.writeUID(taskRun.uid) out.writeUID(taskRun.taskDescriptionId) @@ -37,7 +37,7 @@ class CompetitionRunSerializer(private val competitionSerializer: CompetitionSer } } } - is NonInteractiveCompetitionRun -> { + is NonInteractiveCompetition -> { //TODO } } @@ -45,16 +45,16 @@ class CompetitionRunSerializer(private val competitionSerializer: CompetitionSer } - override fun deserialize(input: DataInput2, available: Int): CompetitionRun { + override fun deserialize(input: DataInput2, available: Int): Competition { return when(val type = input.unpackInt()) { 1 -> { - val run = InteractiveSynchronousCompetitionRun(input.readUTF().UID(), input.readUTF(), competitionSerializer.deserialize(input, available), input.readLong(), input.readLong()) + val run = InteractiveSynchronousCompetition(input.readUTF().UID(), input.readUTF(), competitionSerializer.deserialize(input, available), input.readLong(), input.readLong()) for (i in 0 until input.readInt()) { - val taskRun = run.TaskRun(input.readUID(), input.readUID(), input.readLong(), input.readLong()) + val taskRun = run.Task(input.readUID(), input.readUID(), input.readLong(), input.readLong()) for (j in 0 until input.readInt()) { (taskRun.submissions as MutableList).add(SubmissionSerializer.deserialize(input,available)) } - (run.tasks as MutableList).add(taskRun) + (run.tasks as MutableList).add(taskRun) } run } diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/SubmissionSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/SubmissionSerializer.kt index 3794ccc7b..3b99186e2 100644 --- a/backend/src/main/kotlin/dev/dres/data/serializers/SubmissionSerializer.kt +++ b/backend/src/main/kotlin/dev/dres/data/serializers/SubmissionSerializer.kt @@ -1,9 +1,8 @@ package dev.dres.data.serializers -import dev.dres.data.model.run.ItemSubmission -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus -import dev.dres.data.model.run.TemporalSubmission + +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.utilities.extensions.readUID import dev.dres.utilities.extensions.writeUID import org.mapdb.DataInput2 @@ -18,23 +17,17 @@ object SubmissionSerializer : Serializer { out.packLong(value.timestamp) MediaItemSerializer.serialize(out, value.item) out.packInt(value.status.ordinal) - - when(value){ - is ItemSubmission -> { - out.packInt(0) - } - is TemporalSubmission -> { + is Submission.Item -> out.packInt(0) + is Submission.Temporal -> { out.packInt(1) out.packLong(value.start) out.packLong(value.end) } } - } override fun deserialize(input: DataInput2, available: Int): Submission { - val id = input.readUID() val teamId = input.readUID() val memberId = input.readUID() @@ -42,14 +35,9 @@ object SubmissionSerializer : Serializer { val item = MediaItemSerializer.deserialize(input, available) val status = SubmissionStatus.values()[input.unpackInt()] - return when(input.unpackInt()) { - 0 -> ItemSubmission(teamId, memberId, timestamp, item, id).apply { this.status = status } - 1 -> TemporalSubmission(teamId, memberId, timestamp, item, - input.unpackLong(), - input.unpackLong(), - id - ).apply { this.status = status } + 0 -> Submission.Item(teamId, memberId, timestamp, item, id).apply { this.status = status } + 1 -> Submission.Temporal(teamId, memberId, timestamp, item, input.unpackLong(), input.unpackLong(), id).apply { this.status = status } else -> throw IllegalStateException("Unknown Submission Type") } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 5655b792a..a23723f38 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -6,12 +6,25 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.run.* +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator - -class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronousCompetitionRun) : InteractiveRunManager { - +import java.util.* + +/** + * An implementation of a [RunManager] aimed at distributed execution having a single DRES Server instance and + * multiple clients connected via WebSocket. + * + * As opposed to the [InteractiveSynchronousRunManager], competitions in the [InteractiveAsynchronousRunManager] + * can take place at different points in time for different teams and tasks, i.e., the competitions are executed + * asynchronously. + * + * @version 1.0.0 + * @author Ralph Gasser + */ +class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronousCompetition) : InteractiveRunManager { override fun currentTask(context: RunActionContext): TaskDescription? { @@ -27,10 +40,10 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous override val allSubmissions: List get() = TODO("Not yet implemented") - override val currentTaskRun: InteractiveSynchronousCompetitionRun.TaskRun? + override val currentTaskRun: InteractiveSynchronousCompetition.Task? get() = TODO("Not yet implemented") - override fun tasks(context: RunActionContext): List { + override fun tasks(context: RunActionContext): List { TODO("Not yet implemented") } @@ -65,7 +78,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous override fun taskRunForId( context: RunActionContext, taskRunId: UID - ): InteractiveSynchronousCompetitionRun.TaskRun? { + ): InteractiveSynchronousCompetition.Task? { TODO("Not yet implemented") } @@ -88,7 +101,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous get() = TODO("Not yet implemented") override val competitionDescription: CompetitionDescription - get() = this.run.competitionDescription + get() = this.run.description override val scoreboards: List get() = TODO("Not yet implemented") diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 9de621bc1..828be5437 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -4,6 +4,8 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.run.* +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard @@ -17,7 +19,7 @@ interface InteractiveRunManager : RunManager { fun currentTask(context: RunActionContext): TaskDescription? /** - * List of [Submission]s for the current [InteractiveSynchronousCompetitionRun.TaskRun]. + * List of [Submission]s for the current [InteractiveSynchronousCompetition.Task]. * * Part of the [RunManager]'s execution state. Can be empty! */ @@ -26,17 +28,20 @@ interface InteractiveRunManager : RunManager { /** List of [ScoreTimePoint]s tracking the states of the different [Scoreboard]s over time*/ val scoreHistory: List - /** List of all [Submission]s for this [RunManager], irrespective of the [InteractiveSynchronousCompetitionRun.TaskRun] it belongs to. */ + /** List of all [Submission]s for this [RunManager], irrespective of the [InteractiveSynchronousCompetition.Task] it belongs to. */ val allSubmissions: List /** - * Reference to the [InteractiveSynchronousCompetitionRun.TaskRun] that is currently being executed OR that has just ended. + * Reference to the [InteractiveSynchronousCompetition.Task] that is currently being executed OR that has just ended. * * Part of the [RunManager]'s execution state. Can be null! */ - val currentTaskRun: InteractiveSynchronousCompetitionRun.TaskRun? + val currentTaskRun: InteractiveSynchronousCompetition.Task? - override fun tasks(context: RunActionContext): List + /** + * + */ + override fun tasks(context: RunActionContext): List /** * Prepares this [RunManager] for the execution of previous [Task] as per order defined in [CompetitionDescription.tasks]. @@ -116,12 +121,12 @@ interface InteractiveRunManager : RunManager { fun timeLeft(context: RunActionContext): Long /** - * Returns [InteractiveSynchronousCompetitionRun.TaskRun]s for the specified index. The index is zero based, i.e., - * an index of 0 returns the first [InteractiveSynchronousCompetitionRun.TaskRun], index of 1 the second etc. + * Returns [InteractiveSynchronousCompetition.Task]s for the specified index. The index is zero based, i.e., + * an index of 0 returns the first [InteractiveSynchronousCompetition.Task], index of 1 the second etc. * - * @param taskRunId The [UID] of the desired [InteractiveSynchronousCompetitionRun.TaskRun]. + * @param taskRunId The [UID] of the desired [InteractiveSynchronousCompetition.Task]. */ - fun taskRunForId(context: RunActionContext, taskRunId: UID): InteractiveSynchronousCompetitionRun.TaskRun? + fun taskRunForId(context: RunActionContext, taskRunId: UID): InteractiveSynchronousCompetition.Task? /** * Override the ready state for a given viewer ID. diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 227152541..861828ceb 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -10,6 +10,8 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.run.* +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.run.eventstream.EventStreamProcessor @@ -30,12 +32,12 @@ import kotlin.math.max /** * An implementation of [RunManager] aimed at distributed execution having a single DRES Server instance and multiple - * viewers connected via WebSocket. Before starting a [InteractiveSynchronousCompetitionRun.TaskRun], all viewer instances are synchronized. + * viewers connected via WebSocket. Before starting a [InteractiveSynchronousCompetition.Task], all viewer instances are synchronized. * * @version 2.1.0 * @author Ralph Gasser */ -class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitionRun) : InteractiveRunManager { +class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetition) : InteractiveRunManager { private val VIEWER_TIME_OUT = 30L //TODO make configurable @@ -43,15 +45,13 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio private val LOGGER = LoggerFactory.getLogger(this.javaClass) - /** - * Number of consecutive errors which have to occur within the main execution loop before it tries to gracefully terminate - */ + /** Number of consecutive errors which have to occur within the main execution loop before it tries to gracefully terminate */ private val maxErrorCount = 5 /** - * Alternative constructor from existing [InteractiveSynchronousCompetitionRun]. + * Alternative constructor from existing [InteractiveSynchronousCompetition]. */ - constructor(description: CompetitionDescription, name: String) : this(InteractiveSynchronousCompetitionRun(UID.EMPTY, name, description).apply { RunExecutor.runs.append(this) }) + constructor(description: CompetitionDescription, name: String) : this(InteractiveSynchronousCompetition(UID.EMPTY, name, description).apply { RunExecutor.runs.append(this) }) /** Run ID of this [InteractiveSynchronousRunManager]. */ override val id: UID @@ -63,15 +63,15 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ override val competitionDescription: CompetitionDescription - get() = this.run.competitionDescription + get() = this.run.description /** Reference to the currently active [TaskDescription]. This is part of the task navigation. */ private var currentTask = this.competitionDescription.tasks[0] override fun currentTask(context: RunActionContext): TaskDescription = currentTask - /** Reference to the currently active [InteractiveSynchronousCompetitionRun.TaskRun].*/ - override val currentTaskRun: InteractiveSynchronousCompetitionRun.TaskRun? + /** Reference to the currently active [InteractiveSynchronousCompetition.Task].*/ + override val currentTaskRun: InteractiveSynchronousCompetition.Task? get() = this.stateLock.read { return when (this.status) { RunManagerStatus.PREPARING_TASK, @@ -81,9 +81,10 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } } - override fun tasks(context: RunActionContext): List = this.run.tasks + /** List of [InteractiveSynchronousCompetition.Task] for this [InteractiveSynchronousRunManager]. */ + override fun tasks(context: RunActionContext): List = this.run.tasks - /** The list of [Submission]s for the current [InteractiveSynchronousCompetitionRun.TaskRun]. */ + /** The list of [Submission]s for the current [InteractiveSynchronousCompetition.Task]. */ override val submissions: List get() = this.stateLock.read { this.currentTaskRun?.submissions ?: emptyList() @@ -271,12 +272,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio checkContext(context) /* Create and prepare pipeline for submission. */ - this.run.newTaskRun(this.currentTask(context).id) - - /* Create and prepare pipeline for submission (FIXME: is this used?). */ - //val ret = this.run.newTaskRun(this.currentTask.id) - //val pipeline = Triple(ret.task.newFilter(), ret.task.newValidator(), ret.task.newScorer()) - //(this.submissionPipeline as MutableList).add(pipeline) + this.run.Task(taskDescriptionId = this.currentTask(context).id) /* Update status. */ this.status = RunManagerStatus.PREPARING_TASK @@ -317,21 +313,21 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } /** - * Returns [InteractiveSynchronousCompetitionRun.TaskRun]s for a specific task [UID]. May be empty. + * Returns [InteractiveSynchronousCompetition.Task]s for a specific task [UID]. May be empty. * - * @param taskRunId The [UID] of the [InteractiveSynchronousCompetitionRun.TaskRun]. + * @param taskRunId The [UID] of the [InteractiveSynchronousCompetition.Task]. */ - override fun taskRunForId(context: RunActionContext, taskRunId: UID): InteractiveSynchronousCompetitionRun.TaskRun? = this.run.tasks.find { it.uid == taskRunId } + override fun taskRunForId(context: RunActionContext, taskRunId: UID): InteractiveSynchronousCompetition.Task? = this.run.tasks.find { it.uid == taskRunId } /** - * Returns the number of [InteractiveSynchronousCompetitionRun.TaskRun]s held by this [RunManager]. + * Returns the number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager]. * - * @return The number of [InteractiveSynchronousCompetitionRun.TaskRun]s held by this [RunManager] + * @return The number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager] */ override fun taskCount(context: RunActionContext): Int = this.run.tasks.size /** - * Adjusts the duration of the current [InteractiveSynchronousCompetitionRun.TaskRun] by the specified amount. Amount can be either positive or negative. + * Adjusts the duration of the current [InteractiveSynchronousCompetition.Task] by the specified amount. Amount can be either positive or negative. * * @param s The number of seconds to adjust the duration by. * @return Time remaining until the task will end in milliseconds @@ -351,7 +347,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } /** - * Returns the time in milliseconds that is left until the end of the current [InteractiveSynchronousCompetitionRun.TaskRun]. + * Returns the time in milliseconds that is left until the end of the current [InteractiveSynchronousCompetition.Task]. * Only works if the [RunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, * this method returns -1L. * @@ -417,8 +413,8 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } /** - * Processes incoming [Submission]s. If a [InteractiveSynchronousCompetitionRun.TaskRun] is running then that [Submission] will usually - * be associated with that [InteractiveSynchronousCompetitionRun.TaskRun]. + * Processes incoming [Submission]s. If a [InteractiveSynchronousCompetition.Task] is running then that [Submission] will usually + * be associated with that [InteractiveSynchronousCompetition.Task]. * * This method will not throw an exception and instead return false if a [Submission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke @@ -446,22 +442,20 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } /** - * Processes incoming [Submission]s. If a [InteractiveSynchronousCompetitionRun.TaskRun] is running then that [Submission] will usually - * be associated with that [InteractiveSynchronousCompetitionRun.TaskRun]. + * Processes incoming [Submission]s. If a [InteractiveSynchronousCompetition.Task] is running then that [Submission] will usually + * be associated with that [InteractiveSynchronousCompetition.Task]. * * This method will not throw an exception and instead return false if a [Submission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * - * @param sub [Submission] that should be registered. + * @param suid [UID] that identifies the [Submission]. + * @param newStatus The new [SubmissionStatus] for the [Submission]. */ override fun updateSubmission(suid: UID, newStatus: SubmissionStatus): Boolean = this.stateLock.read { - // We have to ignore anything state related, as we allow updates by admins regardless of state -// check(this.status == RunManagerStatus.RUNNING_TASK) { "SynchronizedRunManager is in status ${this.status} and can currently not accept submissions." } - /* Sanity check */ - val found = submissions.find { it.uid == suid} - ?: return false + val found = this.submissions.find { it.uid == suid} ?: return false + /* Actual update - currently, only status update is allowed */ found.status = newStatus @@ -478,7 +472,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } /** - * Internal method that orchestrates the internal progression of the [InteractiveSynchronousCompetitionRun]. + * Internal method that orchestrates the internal progression of the [InteractiveSynchronousCompetition]. */ override fun run() { /** Sort list of by [Phase] in ascending order. */ diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt similarity index 93% rename from backend/src/main/kotlin/dev/dres/run/NonInteractiveSynchronousRunManager.kt rename to backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index a036767a1..4605a1bb1 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -7,6 +7,8 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.* +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.submissions.batch.SubmissionBatch import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.ScoreboardsUpdatable import dev.dres.run.validation.interfaces.JudgementValidator @@ -16,7 +18,7 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read -class NonInteractiveSynchronousRunManager(val run: NonInteractiveCompetitionRun) : RunManager { +class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager { private val SCOREBOARD_UPDATE_INTERVAL_MS = 10_000L // TODO make configurable @@ -35,7 +37,7 @@ class NonInteractiveSynchronousRunManager(val run: NonInteractiveCompetitionRun) /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ override val competitionDescription: CompetitionDescription - get() = this.run.competitionDescription + get() = this.run.description /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoreboardsUpdatable = ScoreboardsUpdatable(this.competitionDescription.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) @@ -138,6 +140,9 @@ class NonInteractiveSynchronousRunManager(val run: NonInteractiveCompetitionRun) private val updatedTasks = LinkedBlockingQueue>>>() + /** + * + */ fun addSubmissionBatch(batch: SubmissionBatch<*>) = this.stateLock.read{ check(this.status == RunManagerStatus.RUNNING_TASK) { "SynchronousNonInteractiveRunManager is in status ${this.status} and can currently not accept submissions." } @@ -152,5 +157,8 @@ class NonInteractiveSynchronousRunManager(val run: NonInteractiveCompetitionRun) } - override fun tasks(context: RunActionContext): List = this.run.tasks + /** + * + */ + override fun tasks(context: RunActionContext): List = this.run.tasks } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 644128893..3d485cdf1 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -8,9 +8,9 @@ import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.dbo.DAO import dev.dres.data.model.UID -import dev.dres.data.model.run.CompetitionRun -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun -import dev.dres.data.model.run.NonInteractiveCompetitionRun +import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.NonInteractiveCompetition +import dev.dres.data.model.run.interfaces.Competition import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.read @@ -60,20 +60,20 @@ object RunExecutor : Consumer { /** Internal array of [Future]s for cleaning after [RunManager]s. See [RunExecutor.cleanerThread]*/ private val results = HashMap, UID>() - /** Instance of shared [DAO] used to access [InteractiveSynchronousCompetitionRun]s. */ - lateinit var runs: DAO + /** Instance of shared [DAO] used to access [InteractiveSynchronousCompetition]s. */ + lateinit var runs: DAO /** * Initializes this [RunExecutor]. * - * @param runs The shared [DAO] used to access [InteractiveSynchronousCompetitionRun]s. + * @param runs The shared [DAO] used to access [InteractiveSynchronousCompetition]s. */ - fun init(runs: DAO) { + fun init(runs: DAO) { this.runs = runs this.runs.filter { !it.hasEnded }.forEach { //TODO needs more distinction val run = when(it) { - is InteractiveSynchronousCompetitionRun -> InteractiveSynchronousRunManager(it) - is NonInteractiveCompetitionRun -> NonInteractiveSynchronousRunManager(it) + is InteractiveSynchronousCompetition -> InteractiveSynchronousRunManager(it) + is NonInteractiveCompetition -> NonInteractiveRunManager(it) else -> throw NotImplementedError("No matching run manager found for $it") } this.schedule(run) diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 1963df147..b1f27cd28 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -4,20 +4,19 @@ import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.run.websocket.ClientMessage import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun +import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.Task +import dev.dres.data.model.run.interfaces.Task import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator /** - * A managing class for concrete executions of [CompetitionDescription], i.e. [InteractiveSynchronousCompetitionRun]s. + * A managing class for concrete executions of [CompetitionDescription], i.e. [InteractiveSynchronousCompetition]s. * - * @see InteractiveSynchronousCompetitionRun + * @see InteractiveSynchronousCompetition * * @author Ralph Gasser - * @version 1.4.0 + * @version 1.5.0 */ interface RunManager : Runnable { /** Unique, public, numeric ID for this [RunManager]. */ @@ -45,6 +44,7 @@ interface RunManager : Runnable { * As all state affecting methods, this method throws an [IllegalStateException] if invocation * does not match the current state. * + * @param context The [RunActionContext] for this invocation. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.CREATED] */ fun start(context: RunActionContext) @@ -56,18 +56,26 @@ interface RunManager : Runnable { * As all state affecting methods, this method throws an [IllegalStateException] if invocation * does not match the current state. * + * @param context The [RunActionContext] for this invocation. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ fun end(context: RunActionContext) /** - * Returns the number of [InteractiveSynchronousCompetitionRun.TaskRun]s held by this [RunManager]. + * Returns the number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager]. * - * @return The number of [InteractiveSynchronousCompetitionRun.TaskRun]s held by this [RunManager] + * @param context The [RunActionContext] for this invocation. + * @return The number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager] */ fun taskCount(context: RunActionContext): Int + /** + * Returns a list of all [Task]s that took or are taking place in the scope of this [RunManager]. + * + * @param context The [RunActionContext] for this invocation. + * @return List of [Task] that took place (are taking place). + */ fun tasks(context: RunActionContext): List /** @@ -77,7 +85,6 @@ interface RunManager : Runnable { */ fun viewers(): Map - /** * Invoked by an external caller such in order to inform the [RunManager] that it has received a [ClientMessage]. * diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt index 229386f8c..d9c351eb2 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt @@ -2,8 +2,8 @@ package dev.dres.run.audit import dev.dres.data.model.Entity import dev.dres.data.model.UID -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus enum class AuditLogEntryType { diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index b12f63422..35e773643 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -2,8 +2,8 @@ package dev.dres.run.audit import dev.dres.data.dbo.DAO import dev.dres.data.model.UID -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import org.slf4j.LoggerFactory import org.slf4j.Marker import org.slf4j.MarkerFactory diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt index 7fd9c8fc3..6e96c4b21 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt @@ -5,7 +5,7 @@ import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.log.QueryEventLog import dev.dres.data.model.log.QueryResultLog -import dev.dres.data.model.run.Submission +import dev.dres.data.model.submissions.Submission sealed class StreamEvent(var timeStamp : Long = System.currentTimeMillis(), var session: String? = null) diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index 3b8249499..19380dc73 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -1,8 +1,8 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.model.UID -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.eventstream.* import java.io.File import java.io.PrintWriter diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index 36ab7e92c..7fb16a58e 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -2,9 +2,7 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.model.UID import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.run.ItemSubmission -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.TemporalSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* import dev.dres.run.score.interfaces.IncrementalTaskRunScorer import dev.dres.run.score.interfaces.RecalculatingTaskRunScorer @@ -52,8 +50,8 @@ class TeamCombinationScoreHandler : StreamEventHandler { combinations.map { if (it.value.first == submission.teamId || it.value.second == submission.teamId) { when (submission) { - is ItemSubmission -> submission.copy(teamId = it.key).apply { this.status = submission.status } - is TemporalSubmission -> submission.copy(teamId = it.key).apply { this.status = submission.status } + is Submission.Item -> submission.copy(teamId = it.key).apply { this.status = submission.status } + is Submission.Temporal -> submission.copy(teamId = it.key).apply { this.status = submission.status } } } else { null diff --git a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt index 4265f7c22..9dfd737eb 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt @@ -1,6 +1,7 @@ package dev.dres.run.filter -import dev.dres.data.model.run.Submission +import dev.dres.data.model.submissions.Submission + /** * A [SubmissionFilter] that lets all [Submission] pass. diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectSubmissionFilter.kt index 6c8dd3091..05dff6fd9 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectSubmissionFilter.kt @@ -1,7 +1,7 @@ package dev.dres.run.filter -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus class CorrectSubmissionPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index c5a371af8..8c18f9c53 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -1,7 +1,7 @@ package dev.dres.run.filter -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.TemporalSubmissionAspect +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect class DuplicateSubmissionFilter : SubmissionFilter { override fun test(submission: Submission): Boolean = submission.task!!.submissions.none { diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionCountFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionCountFilter.kt index b07358137..65ff4c507 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionCountFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionCountFilter.kt @@ -1,7 +1,7 @@ package dev.dres.run.filter -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus class MaximumTotalSubmissionsPerTeam(private val max: Int = Int.MAX_VALUE) : SubmissionFilter { constructor(parameters: Map) : this(parameters.getOrDefault("limit", "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt index 7e2263a7c..8dcf5f14c 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt @@ -1,6 +1,6 @@ package dev.dres.run.filter -import dev.dres.data.model.run.Submission +import dev.dres.data.model.submissions.Submission import java.util.function.Predicate /** @@ -8,14 +8,10 @@ import java.util.function.Predicate * by the [Submission] evaluation pipeline. * * @author Ralph Gasser - * @version 1.0 + * @version 1.0.0 */ fun interface SubmissionFilter : Predicate { - override infix fun and(other: Predicate): SubmissionFilter = SubmissionFilter { s -> this@SubmissionFilter.test(s) && other.test(s) } - override infix fun or(other: Predicate): SubmissionFilter = SubmissionFilter { s -> this@SubmissionFilter.test(s) || other.test(s) } - operator fun not(): SubmissionFilter = SubmissionFilter { s -> !this@SubmissionFilter.test(s) } - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt index 9e7afd52b..8cdbe7d05 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt @@ -1,9 +1,9 @@ package dev.dres.run.filter -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.TemporalSubmissionAspect +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect + class TemporalSubmissionFilter : SubmissionFilter { override fun test(submission: Submission): Boolean = submission is TemporalSubmissionAspect - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskRunScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskRunScorer.kt index d50816701..549236155 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskRunScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskRunScorer.kt @@ -1,6 +1,6 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.run.Submission +import dev.dres.data.model.submissions.Submission /** * A [RecalculatingTaskRunScorer] that can update scores incrementally on a [Submission] by [Submission] basis. diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskRunScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskRunScorer.kt index debba03f5..2e782848f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskRunScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskRunScorer.kt @@ -2,11 +2,11 @@ package dev.dres.run.score.interfaces import dev.dres.data.model.UID import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun -import dev.dres.data.model.run.Submission +import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.submissions.Submission /** - * Computes the current scores of all teams for a given [InteractiveSynchronousCompetitionRun.TaskRun] + * Computes the current scores of all teams for a given [InteractiveSynchronousCompetition.Task] * * @author Luca Rossetto & Ralph Gasser * @version 1.0.1 diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index f0b5e4297..61009fc50 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -3,9 +3,8 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.UID import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.Team -import dev.dres.data.model.run.InteractiveTask -import dev.dres.data.model.run.Task -import dev.dres.data.model.run.TaskId +import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.interfaces.TaskId import dev.dres.run.score.interfaces.TaskRunScorer import java.util.concurrent.ConcurrentHashMap import kotlin.math.max @@ -41,10 +40,10 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, pri this.scorePerTaskMap.putAll(scorers.map { it.key to it.value.scores() }.toMap()) } - override fun update(runs: List) { + override fun update(runs: List) { update( runs - .filter { taskFilter(it.taskDescription) && if (it is InteractiveTask) (it.started != null) else true } + .filter { taskFilter(it.description) && (it.started != null) } .map { it.uid to it.scorer }.toMap() ) } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt index 565f1d35c..643de19a5 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt @@ -3,8 +3,9 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.UID import dev.dres.data.model.competition.Team import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.Task -import dev.dres.data.model.run.TaskId +import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskId import dev.dres.run.score.interfaces.TaskRunScorer /** @@ -43,7 +44,7 @@ interface Scoreboard { /** * Updates the [Scoreboard]. */ - fun update(runs: List) + fun update(runs: List) /** * Updates using a map of the [TaskRun] ids to the corresponding [TaskRunScorer]s diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt index a0d15caef..4f4207b33 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt @@ -1,8 +1,9 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.Task -import dev.dres.data.model.run.TaskId +import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskId import dev.dres.run.score.interfaces.TaskRunScorer class SumAggregateScoreBoard(override val name: String, private val boards: List, private val taskGroupName: String? = null) : Scoreboard { @@ -14,7 +15,7 @@ class SumAggregateScoreBoard(override val name: String, private val boards: List override fun score(teamId: TeamId) = boards.map { it.score(teamId) }.sum() - override fun update(runs: List) { + override fun update(runs: List) { //since calls are delegated, nothing needs to be done here } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 9d65fbebc..9416652ce 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -3,9 +3,8 @@ package dev.dres.run.score.scorer import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus -import dev.dres.data.model.run.TemporalSubmission +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.interfaces.RecalculatingTaskRunScorer import dev.dres.utilities.TimeUtil import java.util.concurrent.locks.ReentrantReadWriteLock @@ -52,9 +51,7 @@ class AvsTaskScorer: RecalculatingTaskRunScorer { when(it.key) { is MediaItem.ImageItem -> 1 is MediaItem.VideoItem -> { - - val ranges = it.value.map { s -> (s as TemporalSubmission).temporalRange } - + val ranges = it.value.map { s -> (s as Submission.Temporal).temporalRange } TimeUtil.merge(ranges, overlap = 1).size } } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index c4a3c4762..bb0ece364 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -2,8 +2,8 @@ package dev.dres.run.score.scorer import dev.dres.data.model.UID import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.interfaces.RecalculatingTaskRunScorer import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index a8edfd73f..b722d66da 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -2,7 +2,7 @@ package dev.dres.run.updatables import dev.dres.data.model.competition.TaskType import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.InteractiveRunManager import dev.dres.run.RunManagerStatus import java.util.concurrent.atomic.AtomicInteger @@ -15,14 +15,14 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte private var submissions = AtomicInteger(0) override fun update(status: RunManagerStatus) { - val limitingFilter = run.currentTaskRun?.taskDescription?.taskType?.filter?.find{ it.option == TaskType.SubmissionFilterType.LIMIT_CORRECT_PER_TEAM } ?: return + val limitingFilter = run.currentTaskRun?.description?.taskType?.filter?.find{ it.option == TaskType.SubmissionFilterType.LIMIT_CORRECT_PER_TEAM } ?: return val limit = limitingFilter.parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1 if (this.run.timeLeft(context) > 0) { val taskRun = this.run.currentTaskRun if (taskRun != null && this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { /* Determine of all teams have submitted . */ val allDone = this.run.competitionDescription.teams.all { team -> - run.submissions.count { it.teamId == team.uid && it.status == SubmissionStatus.CORRECT } >= limit + this.run.submissions.count { it.teamId == team.uid && it.status == SubmissionStatus.CORRECT } >= limit } /* Do all teams have reached the limit of correct submissions ? */ diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt index fd3e0dab7..abf8e645c 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt @@ -1,6 +1,7 @@ package dev.dres.run.updatables -import dev.dres.data.model.run.CompetitionRun +import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.interfaces.Competition import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus import dev.dres.run.score.ScoreTimePoint @@ -14,7 +15,7 @@ import java.util.* * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -class ScoreboardsUpdatable(val scoreboards: List, val updateIntervalMs: Long, private val run: CompetitionRun): StatefulUpdatable { +class ScoreboardsUpdatable(val scoreboards: List, val updateIntervalMs: Long, private val competition: Competition): StatefulUpdatable { companion object { val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED, RunManagerStatus.PREPARING_TASK) @@ -40,7 +41,7 @@ class ScoreboardsUpdatable(val scoreboards: List, val updateInterval this.dirty = false this.lastUpdate = now this.scoreboards.forEach { - it.update(this.run.tasks) + it.update(this.competition.tasks.filterIsInstance()) it.scores().map{ score -> this._timeSeries.add(ScoreTimePoint(it.name, score)) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index 9daa97278..6c5cb3d32 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -3,9 +3,11 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID -import dev.dres.data.model.run.InteractiveSynchronousCompetitionRun -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.RunManagerStatus import dev.dres.run.score.interfaces.IncrementalTaskRunScorer import dev.dres.run.score.interfaces.RecalculatingTaskRunScorer @@ -13,10 +15,10 @@ import java.util.* /** * This is a [Updatable] that runs necessary post-processing after a [Submission] has been validated; - * it update the scores for the respective [InteractiveSynchronousCompetitionRun.TaskRun]. + * it update the scores for the respective [InteractiveSynchronousCompetition.Task]. * * @author Ralph Gasser - * @version 1.0 + * @version 1.1.0 */ class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdatable, val messageQueueUpdatable: MessageQueueUpdatable): Updatable { @@ -25,17 +27,17 @@ class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdat } /** Internal list of [Submission] that pend processing. */ - private val list = LinkedList>() + private val list = LinkedList>() /** The [Phase] this [ScoresUpdatable] belongs to. */ override val phase: Phase = Phase.MAIN /** Enqueues a new [Submission] for post-processing. */ - fun enqueue(submission: Pair) = this.list.add(submission) + fun enqueue(submission: Pair) = this.list.add(submission) override fun update(status: RunManagerStatus) { if (!this.list.isEmpty()) { - val scorersToUpdate = mutableSetOf>() + val scorersToUpdate = mutableSetOf>() val removed = this.list.removeIf { val scorer = it.first.scorer if (it.second.status != SubmissionStatus.INDETERMINATE) { @@ -54,7 +56,7 @@ class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdat scorersToUpdate.forEach { val task = it.first if (it.first.started != null) { - it.second.computeScores(task.submissions, task.competition.competitionDescription.teams.map { t -> t.uid }, task.started!!, task.duration, task.ended ?: 0) + it.second.computeScores(task.submissions, task.competition.description.teams.map { t -> t.uid }, task.started!!, task.description.duration, task.ended ?: 0) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt index 82a4f576a..738df101e 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.validation.interfaces.SubmissionValidator class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator, private val continueStates: Set, private val secondValidator: SubmissionValidator) : SubmissionValidator { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index eae2bdb44..86a63ebb4 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -1,8 +1,8 @@ package dev.dres.run.validation import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.validation.interfaces.SubmissionValidator class MediaItemsSubmissionValidator(private val items : Set) : SubmissionValidator { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index 4bea41657..aa07ae7a4 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -2,9 +2,9 @@ package dev.dres.run.validation import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.VideoSegment -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus -import dev.dres.data.model.run.TemporalSubmissionAspect +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.run.validation.interfaces.SubmissionValidator import dev.dres.utilities.TimeUtil diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index d44fa1583..50a13a905 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -2,9 +2,9 @@ package dev.dres.run.validation import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.VideoSegment -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus -import dev.dres.data.model.run.TemporalSubmissionAspect +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.run.validation.interfaces.SubmissionValidator import dev.dres.utilities.TimeUtil diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index 9a425b8d6..3882b9674 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -1,8 +1,7 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.run.BaseSubmissionAspect -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.BaseSubmissionAspect /** * A [SubmissionValidator] that bases validation on human (manual) verdicts. This kind of [SubmissionValidator] diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionBatchValidator.kt index 21cbbd641..7797437cd 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionBatchValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionBatchValidator.kt @@ -1,9 +1,7 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.run.ResultBatch +import dev.dres.data.model.submissions.batch.ResultBatch interface SubmissionBatchValidator { - fun validate(batch: ResultBatch<*>) - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionJudgementValidator.kt index e1d020809..d2a1cd647 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionJudgementValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus interface SubmissionJudgementValidator : SubmissionValidator, JudgementValidator { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt index b689ce1fc..24647d8c5 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus /** * A validator class that checks, if a [Submission] is correct. diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt index d71fca624..d1dadf8b5 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus interface VoteValidator : JudgementValidator { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 2d41d407a..e112bdfe0 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.audit.AuditLogger import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.SubmissionJudgementValidator diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index b97ddd032..21f86c0e2 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.validation.interfaces.VoteValidator import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue @@ -68,7 +68,6 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() //siphon of undecidable submission from logic of super class override fun judge(token: String, verdict: SubmissionStatus) { val submission = super.processSubmission(token, verdict) - if (submission != null) { when(verdict){ SubmissionStatus.CORRECT, @@ -78,6 +77,5 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() } } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index 300644e22..d23270152 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -1,8 +1,8 @@ package dev.dres.run.validation.judged import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.run.Submission -import dev.dres.data.model.run.TemporalSubmissionAspect +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect /** Helper class to store submission information independent of source */ data class ItemRange(val item: MediaItem, val start: Long, val end: Long){ diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index a3a9eaf4e..ff7463abf 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -3,7 +3,7 @@ package dres.run.score.scorer import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.run.ItemSubmission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.run.TemporalSubmission import dev.dres.run.score.scorer.AvsTaskScorer import org.junit.jupiter.api.Assertions.assertEquals diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index be72b6deb..a13176fec 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -3,7 +3,7 @@ package dres.run.score.scorer import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.run.ItemSubmission -import dev.dres.data.model.run.SubmissionStatus +import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.scorer.KisTaskScorer import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach From 1666aa174e0be37b7ab25f9e3147fa145c7ab7f6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 5 Mar 2021 21:48:00 +0100 Subject: [PATCH 42/95] Some more refactoring in the InteractiveRunManager interface. --- .../dev/dres/api/cli/CompetitionRunCommand.kt | 4 +- .../handler/CompetitionRunAdminHandler.kt | 41 ++--- .../CompetitionRunClientInfoHandler.kt | 4 +- .../api/rest/handler/CompetitionRunHandler.kt | 39 +++-- .../handler/CompetitionRunScoreHandler.kt | 11 +- .../api/rest/handler/SubmissionHandler.kt | 17 +- .../dev/dres/api/rest/types/run/RunState.kt | 2 +- .../run/InteractiveAsynchronousCompetition.kt | 15 +- .../dev/dres/run/InteractiveRunManager.kt | 150 ++++++++++-------- .../run/InteractiveSynchronousRunManager.kt | 132 ++++++++------- .../dres/run/updatables/EndTaskUpdatable.kt | 28 ++-- 11 files changed, 242 insertions(+), 201 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt index 0353d7d7f..d2c047ca2 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt @@ -52,7 +52,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktComm } if (plain) { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - println("${RunSummary(it.id.string, it.name, it.competitionDescription.description, it.currentTask( + println("${RunSummary(it.id.string, it.name, it.competitionDescription.description, it.currentTaskDescription( RunActionContext.DUMMY_ADMIN)?.name)} (${it.status})") } } else { @@ -67,7 +67,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktComm } body { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - row(it.id.string, it.name, it.competitionDescription.description, it.currentTask( + row(it.id.string, it.name, it.competitionDescription.description, it.currentTaskDescription( RunActionContext.DUMMY_ADMIN)?.name ?: "N/A", it.status) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index 2075b8d2d..2616a27c3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -191,8 +191,8 @@ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandle val rac = runActionContext(ctx, run) try { - if (run.nextTask(rac)) { - return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask(rac)!!.name}'.") + if (run.next(rac)) { + return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTaskDescription(rac)!!.name}'.") } else { throw ErrorStatusException(400, "Task for run $runId could not be changed because there are no tasks left.", ctx) } @@ -235,8 +235,8 @@ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHand val rac = runActionContext(ctx, run) try { - run.goToTask(rac, idx) - return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask(rac)!!.name}'.") + run.goTo(rac, idx) + return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTaskDescription(rac)!!.name}'.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task for run $runId could not be changed because run is in the wrong state (state = ${run.status}).", ctx) } catch (e: IndexOutOfBoundsException) { @@ -270,8 +270,8 @@ class PreviousTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHa val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) val rac = runActionContext(ctx, run) try { - if (run.previousTask(rac)) { - return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTask(rac)!!.name}'.") + if (run.previous(rac)) { + return SuccessStatus("Task for run $runId was successfully moved to '${run.currentTaskDescription(rac)!!.name}'.") } else { throw ErrorStatusException(400, "Task for run $runId could not be changed because there are no tasks left.", ctx) } @@ -307,11 +307,11 @@ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl val rac = runActionContext(ctx, run) try { run.startTask(rac) - AuditLogger.taskStart(run.id, run.currentTask(rac)?.name ?: "n/a", LogEventSource.REST, ctx.sessionId()) - EventStreamProcessor.event(TaskStartEvent(runId, run.currentTaskRun!!.uid, run.currentTask(rac)!!)) - return SuccessStatus("Task '${run.currentTask(rac)!!.name}' for run $runId was successfully started.") + AuditLogger.taskStart(run.id, run.currentTaskDescription(rac).name, LogEventSource.REST, ctx.sessionId()) + EventStreamProcessor.event(TaskStartEvent(runId, run.currentTask(rac)!!.uid, run.currentTaskDescription(rac))) + return SuccessStatus("Task '${run.currentTaskDescription(rac).name}' for run $runId was successfully started.") } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task '${run.currentTask(rac)!!.name}' for run $runId could not be started because run is in the wrong state (state = ${run.status}).", ctx) + throw ErrorStatusException(400, "Task '${run.currentTaskDescription(rac).name}' for run $runId could not be started because run is in the wrong state (state = ${run.status}).", ctx) } catch (e: IllegalAccessError) { throw ErrorStatusException(403, e.message!!, ctx) } @@ -341,12 +341,12 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) val rac = runActionContext(ctx, run) try { - val task = run.currentTask(rac) + val task = run.currentTaskDescription(rac) run.abortTask(rac) AuditLogger.taskEnd(run.id, task?.name ?: "n/a", LogEventSource.REST, ctx.sessionId()) - return SuccessStatus("Task '${run.currentTask(rac)!!.name}' for run $runId was successfully aborted.") + return SuccessStatus("Task '${run.currentTaskDescription(rac)!!.name}' for run $runId was successfully aborted.") } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task '${run.currentTask(rac)!!.name}' for run $runId could not be aborted because run is in the wrong state (state = ${run.status}).", ctx) + throw ErrorStatusException(400, "Task '${run.currentTaskDescription(rac)!!.name}' for run $runId could not be aborted because run is in the wrong state (state = ${run.status}).", ctx) } catch (e: IllegalAccessError) { throw ErrorStatusException(403, e.message!!, ctx) } @@ -418,7 +418,7 @@ class AdjustDurationRunAdminHandler : AbstractCompetitionRunAdminRestHandler(set val rac = runActionContext(ctx, run) try { run.adjustDuration(rac, duration) - AuditLogger.taskModified(run.id, run.currentTask(rac)?.name + AuditLogger.taskModified(run.id, run.currentTaskDescription(rac)?.name ?: "n/a", "Task duration adjusted by ${duration}s.", LogEventSource.REST, ctx.sessionId()) return SuccessStatus("Duration for run $runId was successfully adjusted.") } catch (e: IllegalStateException) { @@ -453,11 +453,12 @@ class ListSubmissionsPerTaskRunAdminHandler : AbstractCompetitionRunAdminRestHan override fun doGet(ctx: Context): List { val runId = runId(ctx) val run = getRun(runId) ?: throw ErrorStatusException(404, "No such run was found: $runId", ctx) + val rac = runActionContext(ctx, run) val taskId = ctx.pathParamMap().getOrElse("taskId") { throw ErrorStatusException(404, "Parameter 'taskId' is missing!'", ctx) }.UID() - return run.submissions.filter { it.task?.description?.id?.equals(taskId) ?: false }.map { SubmissionInfo.withId(it) } + return run.submissions(rac).filter { it.task?.description?.id?.equals(taskId) ?: false }.map { SubmissionInfo.withId(it) } } } @@ -483,15 +484,15 @@ class OverrideSubmissionStatusRunAdminHandler: AbstractCompetitionRunAdminRestHa override fun doPatch(ctx: Context): SubmissionInfo { val runId = runId(ctx) val run = getRun(runId) ?: throw ErrorStatusException(404, "No such run was found: $runId", ctx) + val rac = runActionContext(ctx, run) val toPatchRest = ctx.body() /* Sanity check to see, whether the submission exists */ - run.submissions.find { it.uid == (toPatchRest.id?.UID() ?: UID.EMPTY)} - ?: throw ErrorStatusException(404, "The given submission $toPatchRest was not found", ctx) + run.allSubmissions.find { it.uid == (toPatchRest.id?.UID() ?: UID.EMPTY)} ?: throw ErrorStatusException(404, "The given submission $toPatchRest was not found", ctx) - if(run.updateSubmission(toPatchRest.id!!.UID(), toPatchRest.status) ){ - return SubmissionInfo.withId(run.submissions.find{it.uid == toPatchRest.id.UID() }!!) - }else{ + if (run.updateSubmission(rac, toPatchRest.id!!.UID(), toPatchRest.status)){ + return SubmissionInfo.withId(run.submissions(rac).find{it.uid == toPatchRest.id.UID() }!!) + } else { throw ErrorStatusException(500, "Could not update the submission. Please see the backend's log", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt index f1afd3794..1a5983706 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt @@ -113,14 +113,14 @@ class CompetitionRunClientCurrentTaskInfoHandler : AbstractCompetitionRunClientI override fun doGet(ctx: Context): ClientTaskInfo { val run = getRun(ctx, runId(ctx)) ?: throw ErrorStatusException(404, "Specified run not found", ctx) + val rac = runActionContext(ctx, run) if (run !is InteractiveRunManager) { throw ErrorStatusException(404, "Specified run is not interactive", ctx) } - val task = run.currentTaskRun ?: throw ErrorStatusException(404, "Specified run has no active task", ctx) + val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified run has no active task", ctx) - val rac = runActionContext(ctx, run) return ClientTaskInfo( task.uid.string, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index e3ab6c1ed..85b40ecf2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -198,7 +198,7 @@ class CurrentTaskInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandl throw ErrorStatusException(403, "Access denied.", ctx) } - return TaskInfo(run.currentTask(rac) ?: throw ErrorStatusException(404, "Run $runId has currently no active task.", ctx)) + return TaskInfo(run.currentTaskDescription(rac) ?: throw ErrorStatusException(404, "Run $runId has currently no active task.", ctx)) } } @@ -228,7 +228,7 @@ class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRu val rac = runActionContext(ctx, run) - val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "No active task in run $runId.", ctx) + val task = run.currentTaskDescription(rac) ?: throw ErrorStatusException(404, "No active task in run $runId.", ctx) try { return task.toTaskHint(config) } catch (e: FileNotFoundException) { @@ -272,7 +272,7 @@ class CurrentTaskTargetHandler(private val config: Config, private val collectio val rac = runActionContext(ctx, run) /* Fetch query target and transform it. */ - val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "No active task in run $runId.", ctx) + val task = run.currentTaskDescription(rac) ?: throw ErrorStatusException(404, "No active task in run $runId.", ctx) try { val target = task.toTaskTarget(config, collections) if (target != null) { @@ -307,22 +307,21 @@ class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandle override fun doGet(ctx: Context): List { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) + val rac = runActionContext(ctx, run) if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied.", ctx) } - val rac = runActionContext(ctx, run) - /* Obtain current task run and check status. */ return if (run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTask(rac)?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { - run.submissions.map { SubmissionInfo.blind(it) } + if (run.currentTaskDescription(rac).taskType.options.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + run.submissions(rac).map { SubmissionInfo.blind(it) } } else { - run.submissions.map { SubmissionInfo.withId(it) } + run.submissions(rac).map { SubmissionInfo.withId(it) } } } else { - run.submissions.map { SubmissionInfo(it) } + run.submissions(rac).map { SubmissionInfo(it) } } } } @@ -347,22 +346,22 @@ class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRest override fun doGet(ctx: Context): List { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) + val rac = runActionContext(ctx, run) if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied", ctx) } - val rac = runActionContext(ctx, run) val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() return if (run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTask(rac)?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { - run.submissions.filter { it.timestamp >= timestamp }.map { SubmissionInfo.blind(it) } + if (run.currentTaskDescription(rac).taskType.options.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + run.submissions(rac).filter { it.timestamp >= timestamp }.map { SubmissionInfo.blind(it) } } else { - run.submissions.filter { it.timestamp >= timestamp }.map { SubmissionInfo.withId(it) } + run.submissions(rac).filter { it.timestamp >= timestamp }.map { SubmissionInfo.withId(it) } } } else { - run.submissions.filter { it.timestamp >= timestamp }.map { SubmissionInfo.blind(it) } + run.submissions(rac).filter { it.timestamp >= timestamp }.map { SubmissionInfo.blind(it) } } } } @@ -389,6 +388,7 @@ class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRes override fun doGet(ctx: Context): List { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) + val rac = runActionContext(ctx, run) if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied", ctx) @@ -396,15 +396,14 @@ class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRes val taskId = ctx.pathParamMap()["taskId"]?.UID() ?: throw ErrorStatusException(404, "Missing task id", ctx) - val rac = runActionContext(ctx, run) - return if (run.currentTaskRun?.taskDescriptionId == taskId && run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTask(rac)?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { - run.submissions.map { SubmissionInfo.blind(it) } + return if (run.currentTask(rac)?.description?.id == taskId && run.status == RunManagerStatus.RUNNING_TASK) { + if (run.currentTaskDescription(rac).taskType.options.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + run.submissions(rac).map { SubmissionInfo.blind(it) } } else { - run.submissions.map { SubmissionInfo.withId(it) } + run.submissions(rac).map { SubmissionInfo.withId(it) } } } else { - run.taskRunForId(rac, taskId)?.submissions?.map { SubmissionInfo(it) } ?: emptyList() + run.taskForId(rac, taskId)?.submissions?.map { SubmissionInfo(it) } ?: emptyList() } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index 8e2086a06..76fe64804 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -110,16 +110,15 @@ class CurrentTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler Score(team.uid.string, scores[team.uid] ?: 0.0) } @@ -167,9 +166,9 @@ class HistoryTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler, private val item val collectionParam = map[PARAMETER_NAME_COLLECTION]?.first() val collectionId: UID = when { collectionParam != null -> this.collections.find { it.name == collectionParam }?.id - else -> runManager.currentTask(rac)?.mediaCollectionId + else -> runManager.currentTaskDescription(rac).mediaCollectionId } ?: throw ErrorStatusException(404, "Media collection '$collectionParam' could not be found.", ctx) /* Find media item. */ @@ -88,7 +88,7 @@ class SubmissionHandler (val collections: DAO, private val item val item = this.itemIndex[collectionId to itemParam].firstOrNull() ?: throw ErrorStatusException(404, "Media item '$itemParam (collection = $collectionId)' could not be found.", ctx) - val mapToSegment = runManager.currentTask(rac)?.taskType?.options?.any { it.option == TaskType.Options.MAP_TO_SEGMENT } == true + val mapToSegment = runManager.currentTaskDescription(rac).taskType.options.any { it.option == TaskType.Options.MAP_TO_SEGMENT } == true return when { map.containsKey(PARAMETER_NAME_SHOT) && item is MediaItem.VideoItem -> { @@ -110,7 +110,7 @@ class SubmissionHandler (val collections: DAO, private val item } else -> Submission.Item(team, userId, submissionTime, item) }.also { - it.task = runManager.currentTaskRun + it.task = runManager.currentTask(rac) } } @@ -139,18 +139,19 @@ class SubmissionHandler (val collections: DAO, private val item val run = getActiveRun(userId, ctx) val time = System.currentTimeMillis() val submission = toSubmission(ctx, userId, run, time) + val rac = RunActionContext.runActionContext(ctx, run) + val result = try { - run.postSubmission(submission) + run.postSubmission(rac, submission) } catch (e: IllegalArgumentException) { //is only thrown by submission filter TODO: nicer exception type throw ErrorStatusException(208, "Submission rejected", ctx) } - val rac = RunActionContext.runActionContext(ctx, run) - AuditLogger.submission(run.id, run.currentTask(rac)?.name ?: "no task", submission, LogEventSource.REST, ctx.sessionId(), ctx.req.remoteAddr) - EventStreamProcessor.event(SubmissionEvent(ctx.sessionId(), run.id, run.currentTaskRun?.uid, submission)) + AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, submission, LogEventSource.REST, ctx.sessionId(), ctx.req.remoteAddr) + EventStreamProcessor.event(SubmissionEvent(ctx.sessionId(), run.id, run.currentTask(rac)?.uid, submission)) - if (run.currentTask(rac)?.taskType?.options?.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { //pre-generate preview + if (run.currentTaskDescription(rac).taskType.options.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { //pre-generate preview generatePreview(submission) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt index 968d03696..e403bfd8d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt @@ -15,6 +15,6 @@ import dev.dres.run.RunManagerStatus * @version 1.0.2 */ data class RunState(val id: String, val status: RunManagerStatus, val currentTask: TaskInfo?, val timeLeft: Long) { - constructor(run: InteractiveRunManager, context: RunActionContext) : this(run.id.string, run.status, run.currentTask(context)?.let { TaskInfo(it) }, run.timeLeft(context) / 1000) + constructor(run: InteractiveRunManager, context: RunActionContext) : this(run.id.string, run.status, run.currentTaskDescription(context)?.let { TaskInfo(it) }, run.timeLeft(context) / 1000) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index 718f60456..c66efd919 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -35,9 +35,16 @@ class InteractiveAsynchronousCompetition(override var id: CompetitionId, overrid private val tasksMap = ConcurrentHashMap>() /** A [List] of all active [Task]s.*/ - override val tasks: List + override val tasks: List get() = this.tasksMap.values.flatten() + /** + * Returns the current [Task] for the given [TeamId]. + * + * @param teamId The [TeamId] to lookup. + */ + fun currentTaskForTeam(teamId: TeamId): Task? = this.tasksMap[teamId]?.last() + /** * Generates and returns a [String] representation for this [InteractiveAsynchronousCompetition]. */ @@ -49,7 +56,7 @@ class InteractiveAsynchronousCompetition(override var id: CompetitionId, overrid * @author Ralph Gasser * @version 1.0.0 */ - inner class Task internal constructor (override val uid: TaskId = UID(), val teamId: TeamId, val taskDescriptionId: TaskDescriptionId): AbstractInteractiveTask() { + inner class Task internal constructor (override val uid: TaskId = UID(), val teamId: TeamId, val descriptionId: TaskDescriptionId): AbstractInteractiveTask() { internal constructor(uid: TaskId, teamId: TeamId, taskId: TaskDescriptionId, started: Long, ended: Long): this(uid, teamId, taskId) { this.started = if (started == -1L) { null } else { started } @@ -65,8 +72,8 @@ class InteractiveAsynchronousCompetition(override var id: CompetitionId, overrid get() = this@InteractiveAsynchronousCompetition.tasksMap[this.teamId]?.indexOf(this) ?: -1 @Transient - override val description: TaskDescription = this@InteractiveAsynchronousCompetition.description.tasks.find { it.id == this.taskDescriptionId } - ?: throw IllegalArgumentException("Task with taskId ${this.taskDescriptionId} not found.") + override val description: TaskDescription = this@InteractiveAsynchronousCompetition.description.tasks.find { it.id == this.descriptionId } + ?: throw IllegalArgumentException("Task with taskId ${this.descriptionId} not found.") @Transient override val filter: SubmissionFilter = this.description.newFilter() diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 828be5437..321a736d6 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -4,6 +4,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.run.* +import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.ScoreTimePoint @@ -11,126 +12,140 @@ import dev.dres.run.score.scoreboard.Scoreboard interface InteractiveRunManager : RunManager { - /** - * Reference to the currently active [TaskDescription]. - * - * Part of the [RunManager]'s navigational state. - */ - fun currentTask(context: RunActionContext): TaskDescription? - - /** - * List of [Submission]s for the current [InteractiveSynchronousCompetition.Task]. - * - * Part of the [RunManager]'s execution state. Can be empty! - */ - val submissions: List //TODO needs to be changed to work with asynchronous runs - /** List of [ScoreTimePoint]s tracking the states of the different [Scoreboard]s over time*/ val scoreHistory: List - /** List of all [Submission]s for this [RunManager], irrespective of the [InteractiveSynchronousCompetition.Task] it belongs to. */ + /** List of all [Submission]s for this [InteractiveRunManager], irrespective of the [InteractiveSynchronousCompetition.Task] it belongs to. */ val allSubmissions: List - /** - * Reference to the [InteractiveSynchronousCompetition.Task] that is currently being executed OR that has just ended. - * - * Part of the [RunManager]'s execution state. Can be null! - */ - val currentTaskRun: InteractiveSynchronousCompetition.Task? - /** * + * @param context The [RunActionContext] used for the invocation. + * @return [List] of [AbstractInteractiveTask]s */ override fun tasks(context: RunActionContext): List /** - * Prepares this [RunManager] for the execution of previous [Task] as per order defined in [CompetitionDescription.tasks]. - * Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. + * Prepares this [InteractiveRunManager] for the execution of previous [TaskDescription] as per order defined in + * [CompetitionDescription.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * - * As all state affecting methods, this method throws an [IllegalStateException] if invocation - * does not match the current state. + * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws + * an [IllegalStateException] if invocation does not match the current state. * + * @param context The [RunActionContext] used for the invocation. * @return True if [Task] was moved, false otherwise. Usually happens if last [Task] has been reached. - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] */ - fun previousTask(context: RunActionContext): Boolean + fun previous(context: RunActionContext): Boolean /** - * Prepares this [RunManager] for the execution of next [Task] as per order defined in [CompetitionDescription.tasks]. - * Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. + * Prepares this [InteractiveRunManager] for the execution of next [TaskDescription] as per order defined in + * [CompetitionDescription.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * - * As all state affecting methods, this method throws an [IllegalStateException] if invocation - * does not match the current state. + * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws + * an [IllegalStateException] if invocation oes not match the current state. * + * @param context The [RunActionContext] used for the invocation. * @return True if [Task] was moved, false otherwise. Usually happens if last [Task] has been reached. - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] */ - fun nextTask(context: RunActionContext): Boolean + fun next(context: RunActionContext): Boolean /** - * Prepares this [RunManager] for the execution of the [Task] given by the index as per order + * Prepares this [InteractiveRunManager] for the execution of the [TaskDescription] given by the index as per order * defined in [CompetitionDescription.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * - * As all state affecting methods, this method throws an [IllegalStateException] if invocation - * does not match the current state. + * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws + * an [IllegalStateException] if invocation does not match the current state. + * + * @param context The [RunActionContext] used for the invocation. + * @param index The index to navigate to. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] + */ + fun goTo(context: RunActionContext, index: Int) + + /** + * Reference to the currently active [TaskDescription]. This is part of the [InteractiveRunManager]'s + * navigational state. * - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] + * @param context The [RunActionContext] used for the invocation. + * @return [TaskDescription] */ - fun goToTask(context: RunActionContext, index: Int) + fun currentTaskDescription(context: RunActionContext): TaskDescription /** - * Starts the [RunManager.currentTask] and thus moves the [RunManager.status] from + * Starts the [currentTask] and thus moves the [RunManager.status] from * [RunManagerStatus.ACTIVE] to either [RunManagerStatus.PREPARING_TASK] or [RunManagerStatus.RUNNING_TASK] * - * As all state affecting methods, this method throws an [IllegalStateException] if invocation - * does not match the current state. + * As all state affecting methods, this method throws an [IllegalStateException] if invocation oes not match the current state. * - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] or [RunManager.currentTask] is not set. + * @param context The [RunActionContext] used for the invocation. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] or [currentTask] is not set. */ fun startTask(context: RunActionContext) /** - * Force-abort the [RunManager.currentTask] and thus moves the [RunManager.status] from + * Force-abort the [currentTask] and thus moves the [RunManager.status] from * [RunManagerStatus.PREPARING_TASK] or [RunManagerStatus.RUNNING_TASK] to [RunManagerStatus.ACTIVE] * - * As all state affecting methods, this method throws an [IllegalStateException] if invocation - * does not match the current state. + * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. * - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + * @param context The [RunActionContext] used for the invocation. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ fun abortTask(context: RunActionContext) /** - * Adjusts the duration of the current [TaskRun] by the specified amount. Amount can be positive or negative. + * Adjusts the duration of the current [AbstractInteractiveTask] by the specified amount. Amount can be positive or negative. * + * @param context The [RunActionContext] used for the invocation. * @param s The number of seconds to adjust the duration by. * @return Time remaining until the task will end. * * @throws IllegalArgumentException If the specified correction cannot be applied. - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ fun adjustDuration(context: RunActionContext, s: Int): Long /** * Returns the time in milliseconds that is left until the end of the currently running task. - * Only works if the [RunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, + * Only works if the [InteractiveRunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, * this method returns -1L. * + * @param context The [RunActionContext] used for the invocation. * @return Time remaining until the task will end or -1, if no task is running. */ fun timeLeft(context: RunActionContext): Long /** - * Returns [InteractiveSynchronousCompetition.Task]s for the specified index. The index is zero based, i.e., - * an index of 0 returns the first [InteractiveSynchronousCompetition.Task], index of 1 the second etc. + * Returns a reference to the currently active [AbstractInteractiveTask]. + * + * @param context The [RunActionContext] used for the invocation. + * @return [AbstractInteractiveTask] that is currently active or null, if no such task is active. + */ + fun currentTask(context: RunActionContext): AbstractInteractiveTask? + + /** + * Returns [AbstractInteractiveTask]s for the specified index. The index is zero based, i.e., an index of 0 returns the + * first [AbstractInteractiveTask], index of 1 the second etc. * - * @param taskRunId The [UID] of the desired [InteractiveSynchronousCompetition.Task]. + * @param context The [RunActionContext] used for the invocation. + * @param taskId The [UID] of the desired [AbstractInteractiveTask]. */ - fun taskRunForId(context: RunActionContext, taskRunId: UID): InteractiveSynchronousCompetition.Task? + fun taskForId(context: RunActionContext, taskId: UID): AbstractInteractiveTask? + + /** + * List of [Submission]s for the current [AbstractInteractiveTask]. + * + * @param context The [RunActionContext] used for the invocation. + * @return List of [Submission] for the current, [AbstractInteractiveTask] + */ + fun submissions(context: RunActionContext): List /** * Override the ready state for a given viewer ID. * + * @param context The [RunActionContext] used for the invocation. * @param viewerId The ID of the viewer that should be overridden. * @return true on success, false otherwise */ @@ -138,33 +153,36 @@ interface InteractiveRunManager : RunManager { /** * Invoked by an external caller to post a new [Submission] for the [Task] that is currently being - * executed by this [RunManager]. [Submission]s usually cause updates to the internal state and/or - * the [Scoreboard] of this [RunManager]. + * executed by this [InteractiveRunManager]. [Submission]s usually cause updates to the internal state and/or + * the [Scoreboard] of this [InteractiveRunManager]. * * This method will not throw an exception and instead returns false if a [Submission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * + * + * @param context The [RunActionContext] used for the invocation * @param sub The [Submission] to be posted. + * * @return [SubmissionStatus] of the [Submission] - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun postSubmission(sub: Submission): SubmissionStatus + fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus /** * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [SubmissionStatus]. - * [Submission]s usually cause updates to the internal state and/or - * the [Scoreboard] of this [RunManager]. + * [Submission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveRunManager]. * * This method will not throw an exception and instead returns false if a [Submission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * - * @param sub The [Submission] to be posted. - * @return Whether the update was successfuly or not - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + * @param context The [RunActionContext] used for the invocation + * @param submissionId The [UID] of the [Submission] to update. + * @param submissionStatus The new [SubmissionStatus] + * + * @return Whether the update was successful or not + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun updateSubmission(suid: UID, newStatus: SubmissionStatus): Boolean - - + fun updateSubmission(context: RunActionContext, submissionId: UID, submissionStatus: SubmissionStatus): Boolean } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 861828ceb..ecb38eba6 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -66,30 +66,11 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio get() = this.run.description /** Reference to the currently active [TaskDescription]. This is part of the task navigation. */ - private var currentTask = this.competitionDescription.tasks[0] - override fun currentTask(context: RunActionContext): TaskDescription = currentTask - - - /** Reference to the currently active [InteractiveSynchronousCompetition.Task].*/ - override val currentTaskRun: InteractiveSynchronousCompetition.Task? - get() = this.stateLock.read { - return when (this.status) { - RunManagerStatus.PREPARING_TASK, - RunManagerStatus.RUNNING_TASK, - RunManagerStatus.TASK_ENDED -> this.run.lastTask - else -> null - } - } + private var currentTaskDescription = this.competitionDescription.tasks[0] /** List of [InteractiveSynchronousCompetition.Task] for this [InteractiveSynchronousRunManager]. */ override fun tasks(context: RunActionContext): List = this.run.tasks - /** The list of [Submission]s for the current [InteractiveSynchronousCompetition.Task]. */ - override val submissions: List - get() = this.stateLock.read { - this.currentTaskRun?.submissions ?: emptyList() - } - /** The list of all [Submission]s tracked ever received by this [InteractiveSynchronousRunManager]. */ override val allSubmissions: List get() = this.stateLock.read { @@ -218,9 +199,14 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio LOGGER.info("SynchronousRunManager ${this.id} terminated") } - override fun previousTask(context: RunActionContext): Boolean = this.stateLock.write { + override fun currentTaskDescription(context: RunActionContext): TaskDescription = this.stateLock.write { + check(this.status != RunManagerStatus.TERMINATED) { "SynchronizedRunManager is in status ${this.status} and cannot be terminated." } + this.currentTaskDescription + } + + override fun previous(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.competitionDescription.tasks.indexOf(this.currentTask) - 1 + val newIndex = this.competitionDescription.tasks.indexOf(this.currentTaskDescription) - 1 return try { this.goToTask(newIndex) true @@ -229,9 +215,9 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } } - override fun nextTask(context: RunActionContext): Boolean = this.stateLock.write { + override fun next(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.competitionDescription.tasks.indexOf(this.currentTask) + 1 + val newIndex = this.competitionDescription.tasks.indexOf(this.currentTaskDescription) + 1 return try { this.goToTask(newIndex) true @@ -240,7 +226,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } } - override fun goToTask(context: RunActionContext, index: Int) { + override fun goTo(context: RunActionContext, index: Int) { checkContext(context) goToTask(index) } @@ -250,7 +236,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio if (index >= 0 && index < this.competitionDescription.tasks.size) { /* Update active task. */ - this.currentTask = this.competitionDescription.tasks[index] + this.currentTaskDescription = this.competitionDescription.tasks[index] /* Update RunManager status. */ this.status = RunManagerStatus.ACTIVE @@ -272,7 +258,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio checkContext(context) /* Create and prepare pipeline for submission. */ - this.run.Task(taskDescriptionId = this.currentTask(context).id) + this.run.Task(taskDescriptionId = this.currentTaskDescription(context).id) /* Update status. */ this.status = RunManagerStatus.PREPARING_TASK @@ -287,7 +273,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_PREPARE)) - LOGGER.info("SynchronousRunManager ${this.id} started task task ${this.currentTask}") + LOGGER.info("SynchronousRunManager ${this.id} started task task ${this.currentTaskDescription}") } override fun abortTask(context: RunActionContext) = this.stateLock.write { @@ -296,8 +282,8 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio throw IllegalStateException("SynchronizedRunManager is in status ${this.status}. Tasks can therefore not be aborted.") } - /* End TaskRun and persist. */ - this.currentTaskRun?.end() + /* End TaskRun and persist. */ + this.currentTask(context)?.end() /* Update state. */ this.status = RunManagerStatus.TASK_ENDED @@ -309,15 +295,38 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_END)) - LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.currentTask}") + LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.currentTaskDescription}") + } + + /** + * Returns the currently active [InteractiveSynchronousCompetition.Task]s or null, if no such task is active. + * + * @param context The [RunActionContext] used for the invocation. + * @return [InteractiveSynchronousCompetition.Task] or null + */ + override fun currentTask(context: RunActionContext) = this.stateLock.read { + when (this.status) { + RunManagerStatus.PREPARING_TASK, + RunManagerStatus.RUNNING_TASK, + RunManagerStatus.TASK_ENDED -> this.run.lastTask + else -> null + } } /** * Returns [InteractiveSynchronousCompetition.Task]s for a specific task [UID]. May be empty. * - * @param taskRunId The [UID] of the [InteractiveSynchronousCompetition.Task]. + * @param taskId The [UID] of the [InteractiveSynchronousCompetition.Task]. */ - override fun taskRunForId(context: RunActionContext, taskRunId: UID): InteractiveSynchronousCompetition.Task? = this.run.tasks.find { it.uid == taskRunId } + override fun taskForId(context: RunActionContext, taskId: UID): InteractiveSynchronousCompetition.Task? = this.run.tasks.find { it.uid == taskId } + + /** + * Returns the [Submission]s for all currently active [InteractiveSynchronousCompetition.Task]s or an empty [List], if no such task is active. + * + * @param context The [RunActionContext] used for the invocation. + * @return List of [Submission]s for the currently active [InteractiveSynchronousCompetition.Task] + */ + override fun submissions(context: RunActionContext): List = this.currentTask(context)?.submissions ?: emptyList() /** * Returns the number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager]. @@ -339,7 +348,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio checkContext(context) check(this.status == RunManagerStatus.RUNNING_TASK) { "SynchronizedRunManager is in status ${this.status}. Duration of task can therefore not be adjusted." } - val currentTaskRun = this.currentTaskRun ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") + val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") val newDuration = currentTaskRun.duration + s check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } currentTaskRun.duration = newDuration @@ -353,11 +362,9 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio * * @return Time remaining until the task will end or -1, if no task is running. */ - override fun timeLeft(context: RunActionContext): Long = timeLeft() - - fun timeLeft(): Long = this.stateLock.read { + override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { if (this.status == RunManagerStatus.RUNNING_TASK) { - val currentTaskRun = this.currentTaskRun ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") + val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") return max(0L, currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) } else { -1L @@ -420,13 +427,14 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * + * @param context The [RunActionContext] used for the invocation * @param sub [Submission] that should be registered. */ - override fun postSubmission(sub: Submission): SubmissionStatus = this.stateLock.read { - check(this.status == RunManagerStatus.RUNNING_TASK) { "SynchronousInteractiveRunManager is in status ${this.status} and can currently not accept submissions." } + override fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus = this.stateLock.read { + check(this.status == RunManagerStatus.RUNNING_TASK) { "Run manager is in status ${this.status} and can currently not accept submissions." } /* Register submission. */ - val task = this.currentTaskRun!! + val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") task.addSubmission(sub) /* Mark dao for update. */ @@ -449,26 +457,32 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * - * @param suid [UID] that identifies the [Submission]. - * @param newStatus The new [SubmissionStatus] for the [Submission]. + * @param context The [RunActionContext] used for the invocation + * @param submissionId The [UID] of the [Submission] to update. + * @param submissionStatus The new [SubmissionStatus] + * @return True on success, false otherwise. */ - override fun updateSubmission(suid: UID, newStatus: SubmissionStatus): Boolean = this.stateLock.read { - /* Sanity check */ - val found = this.submissions.find { it.uid == suid} ?: return false + override fun updateSubmission(context: RunActionContext, submissionId: UID, submissionStatus: SubmissionStatus): Boolean = this.stateLock.read { + /* Sanity check. TODO: Do we indeed only want to be able to update submissions for the current task? */ + val found = this.submissions(context).find { it.uid == submissionId} ?: return false /* Actual update - currently, only status update is allowed */ - found.status = newStatus + if (found.status != submissionStatus) { + found.status = submissionStatus - /* Mark dao for update. */ - this.daoUpdatable.dirty = true + /* Mark DAO for update. */ + this.daoUpdatable.dirty = true - /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(found.task!!, found)) + /* Enqueue submission for post-processing. */ + this.scoresUpdatable.enqueue(Pair(found.task!!, found)) - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_UPDATED)) + /* Enqueue WS message for sending */ + this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_UPDATED)) - return true + return true + } + + return false } /** @@ -557,9 +571,9 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ if (this.status == RunManagerStatus.PREPARING_TASK && this.readyLatch.allReadyOrTimedOut()) { this.stateLock.write { - this.currentTaskRun?.start() + this.run.lastTask!!.start() this.status = RunManagerStatus.RUNNING_TASK - AuditLogger.taskStart(this.id, this.currentTask.name, LogEventSource.INTERNAL, null) + AuditLogger.taskStart(this.id, this.currentTaskDescription.name, LogEventSource.INTERNAL, null) } /* Mark DAO for update. */ @@ -571,13 +585,13 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ if (this.status == RunManagerStatus.RUNNING_TASK) { - val timeLeft = this.timeLeft() + val task = this.run.lastTask!! + val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!)) if (timeLeft <= 0) { this.stateLock.write { - val task = this.currentTaskRun!! task.end() this.status = RunManagerStatus.TASK_ENDED - AuditLogger.taskEnd(this.id, this.currentTask.name, LogEventSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, this.currentTaskDescription.name, LogEventSource.INTERNAL, null) EventStreamProcessor.event(TaskEndEvent(this.id, task.uid)) } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index b722d66da..69b50d36a 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -15,20 +15,22 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte private var submissions = AtomicInteger(0) override fun update(status: RunManagerStatus) { - val limitingFilter = run.currentTaskRun?.description?.taskType?.filter?.find{ it.option == TaskType.SubmissionFilterType.LIMIT_CORRECT_PER_TEAM } ?: return - val limit = limitingFilter.parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1 - if (this.run.timeLeft(context) > 0) { - val taskRun = this.run.currentTaskRun - if (taskRun != null && this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { - /* Determine of all teams have submitted . */ - val allDone = this.run.competitionDescription.teams.all { team -> - this.run.submissions.count { it.teamId == team.uid && it.status == SubmissionStatus.CORRECT } >= limit - } + val taskRun = this.run.currentTask(this.context) + if (taskRun != null) { + val limitingFilter = taskRun.description.taskType.filter.find{ it.option == TaskType.SubmissionFilterType.LIMIT_CORRECT_PER_TEAM } ?: return + val limit = limitingFilter.parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1 + if (this.run.timeLeft(context) > 0) { + if (this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { + /* Determine of all teams have submitted . */ + val allDone = this.run.competitionDescription.teams.all { team -> + this.run.submissions(this.context).count { it.teamId == team.uid && it.status == SubmissionStatus.CORRECT } >= limit + } - /* Do all teams have reached the limit of correct submissions ? */ - if (allDone) { - this.run.abortTask(context) - this.submissions.set(0) + /* Do all teams have reached the limit of correct submissions ? */ + if (allDone) { + this.run.abortTask(context) + this.submissions.set(0) + } } } } From e5b8b5a70c8f1c4110b56b17cec2202e254edb83 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 5 Mar 2021 21:48:31 +0100 Subject: [PATCH 43/95] Added a first, incomplete version of the InteractiveAsynchronousRunManager --- .../run/InteractiveAsynchronousRunManager.kt | 210 +++++++++++++++--- 1 file changed, 178 insertions(+), 32 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index a23723f38..81b381a2d 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -2,16 +2,28 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.run.websocket.ClientMessage +import dev.dres.api.rest.types.run.websocket.ServerMessage +import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.* import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard +import dev.dres.run.updatables.DAOUpdatable +import dev.dres.run.updatables.MessageQueueUpdatable +import dev.dres.run.updatables.ScoreboardsUpdatable +import dev.dres.run.updatables.ScoresUpdatable import dev.dres.run.validation.interfaces.JudgementValidator -import java.util.* +import org.slf4j.LoggerFactory +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.collections.HashMap +import kotlin.concurrent.read +import kotlin.concurrent.write +import kotlin.math.max /** * An implementation of a [RunManager] aimed at distributed execution having a single DRES Server instance and @@ -26,13 +38,48 @@ import java.util.* */ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronousCompetition) : InteractiveRunManager { - - override fun currentTask(context: RunActionContext): TaskDescription? { - TODO("Not yet implemented") + companion object { + private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousRunManager::class.java) + private val SCOREBOARD_UPDATE_INTERVAL_MS = 1000L // TODO make configurable } - override val submissions: List - get() = TODO("Not yet implemented") + /** Tracks the current [TaskDescription] per [TeamId]. */ + private val navigationMap: MutableMap = HashMap() + + /** Tracks the current [TaskDescription] per [TeamId]. */ + private val statusMap: MutableMap = HashMap() + + /** A lock for state changes to this [InteractiveAsynchronousRunManager]. */ + private val stateLock = ReentrantReadWriteLock() + + /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.competitionDescription.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + + /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ + private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) + + /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ + private val scoresUpdatable = ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable) + + /** The internal [DAOUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ + private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) + + /** Run ID of this [InteractiveAsynchronousCompetition]. */ + override val id: UID + get() = this.run.id + + /** Name of this [InteractiveAsynchronousCompetition]. */ + override val name: String + get() = this.run.name + + /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ + override val competitionDescription: CompetitionDescription + get() = this.run.description + + override fun currentTaskDescription(context: RunActionContext): TaskDescription { + require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager."} + return this.navigationMap[context.teamId] ?: throw IllegalStateException("Could not find active task description for team ${context.teamId}.") + } override val scoreHistory: List get() = TODO("Not yet implemented") @@ -40,68 +87,167 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous override val allSubmissions: List get() = TODO("Not yet implemented") - override val currentTaskRun: InteractiveSynchronousCompetition.Task? - get() = TODO("Not yet implemented") + init { + /* Initialize map and set all tasks pointers to the first task. */ + this.competitionDescription.teams.forEach { + this.navigationMap[it.uid] = this.competitionDescription.tasks[0] + this.statusMap[it.uid] = if (this.run.hasStarted) { RunManagerStatus.ACTIVE } else { RunManagerStatus.CREATED } + } + } + override fun tasks(context: RunActionContext): List { TODO("Not yet implemented") } - override fun previousTask(context: RunActionContext): Boolean { - TODO("Not yet implemented") + /** + * Prepares this [InteractiveAsynchronousCompetition] for the execution of previous [TaskDescription] + * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting team + * to be [RunManagerStatus.ACTIVE]. + * + * As all state affecting methods, this method throws an [IllegalStateException] if invocation + * does not match the current state. + * + * @param context The [RunActionContext] used for the invocation. + * @return True if [TaskDescription] was moved, false otherwise. Usually happens if last [TaskDescription] has been reached. + * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] + */ + override fun previous(context: RunActionContext): Boolean = this.stateLock.write { + val newIndex = this.competitionDescription.tasks.indexOf(this.currentTaskDescription(context)) + 1 + return try { + this.goTo(context, newIndex) + true + } catch (e: IndexOutOfBoundsException) { + false + } } - override fun nextTask(context: RunActionContext): Boolean { - TODO("Not yet implemented") + /** + * Prepares this [InteractiveAsynchronousCompetition] for the execution of next [TaskDescription] + * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting + * team to be [RunManagerStatus.ACTIVE]. + * + * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. + * + * @param context The [RunActionContext] used for the invocation. + * @return True if [TaskDescription] was moved, false otherwise. Usually happens if last [TaskDescription] has been reached. + * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] + */ + override fun next(context: RunActionContext): Boolean = this.stateLock.write { + val newIndex = this.competitionDescription.tasks.indexOf(this.currentTaskDescription(context)) + 1 + return try { + this.goTo(context, newIndex) + true + } catch (e: IndexOutOfBoundsException) { + false + } } - override fun goToTask(context: RunActionContext, index: Int) { - TODO("Not yet implemented") + /** + * Prepares this [InteractiveAsynchronousCompetition] for the execution of [TaskDescription] with the given [index] + * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting + * team to be [RunManagerStatus.ACTIVE]. + * + * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. + * + * @param context The [RunActionContext] used for the invocation. + * @return True if [TaskDescription] was moved, false otherwise. Usually happens if last [TaskDescription] has been reached. + * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] + */ + override fun goTo(context: RunActionContext, index: Int) = this.stateLock.write { + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} + check(this.statusMap[context.teamId] == RunManagerStatus.ACTIVE || this.status == RunManagerStatus.TASK_ENDED) { "Run manager for team ${context.teamId} is in status ${this.status}. Tasks can therefore not be changed." } + if (index >= 0 && index < this.competitionDescription.tasks.size) { + + /* Update active task. */ + this.navigationMap[context.teamId] = this.competitionDescription.tasks[index] + this.statusMap[context.teamId] = RunManagerStatus.ACTIVE + + /* Mark scoreboards for update. */ + this.scoreboardsUpdatable.dirty = true + + /* Enqueue WS message for sending */ + this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.COMPETITION_UPDATE)) + + LOGGER.info("SynchronousRunManager ${this.id} set to task $index") + } else { + throw IndexOutOfBoundsException("Index $index is out of bounds for the number of available tasks.") + } + } - override fun startTask(context: RunActionContext) { - TODO("Not yet implemented") + override fun startTask(context: RunActionContext) = this.stateLock.write { + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} + check(this.statusMap[context.teamId] == RunManagerStatus.ACTIVE) { "Run manager for team ${context.teamId} is in status ${this.status}. New task cannot be started." } + val currentTask = this.navigationMap[context.teamId] + check(currentTask != null) { } + + /* Create and prepare pipeline for submission. */ + this.run.Task(teamId = context.teamId, descriptionId = this.navigationMap[context.teamId]!!.id) + + /* Update status. */ + this.statusMap[context.teamId] = RunManagerStatus.PREPARING_TASK + + /* Mark scoreboards and DAO for update. */ + this.scoreboardsUpdatable.dirty = true + this.daoUpdatable.dirty = true + + LOGGER.info("SynchronousRunManager ${this.id} started task task $currentTask.") } - override fun abortTask(context: RunActionContext) { + override fun abortTask(context: RunActionContext) = this.stateLock.write { TODO("Not yet implemented") } - override fun adjustDuration(context: RunActionContext, s: Int): Long { - TODO("Not yet implemented") + override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} + if (this.statusMap[context.teamId] == RunManagerStatus.RUNNING_TASK) { + val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("Run manager is in status ${this.status} but has no active task. This is a programmer's error!") + return max(0L, currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) + } else { + -1L + } } - override fun timeLeft(context: RunActionContext): Long { + override fun currentTask(context: RunActionContext): AbstractInteractiveTask? { TODO("Not yet implemented") } - override fun taskRunForId( + override fun taskForId( context: RunActionContext, - taskRunId: UID + taskId: UID ): InteractiveSynchronousCompetition.Task? { TODO("Not yet implemented") } - override fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean { + override fun submissions(context: RunActionContext): List { TODO("Not yet implemented") } - override fun postSubmission(sub: Submission): SubmissionStatus { - TODO("Not yet implemented") + /** + * Overriding the ready state is not supported by the [InteractiveAsynchronousRunManager]s. + * + * @return false + */ + override fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean { + return false } - override fun updateSubmission(suid: UID, newStatus: SubmissionStatus): Boolean { + override fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus { TODO("Not yet implemented") } - override val id: UID - get() = TODO("Not yet implemented") + override fun updateSubmission(context: RunActionContext, submissionId: UID, submissionStatus: SubmissionStatus): Boolean { + TODO("Not yet implemented") + } - override val name: String - get() = TODO("Not yet implemented") + /** + * Adjusting task durations is not supported by the [InteractiveAsynchronousRunManager]s. + * + * @return Time left (see [timeLeft]). + */ + override fun adjustDuration(context: RunActionContext, s: Int): Long = this.timeLeft(context) - override val competitionDescription: CompetitionDescription - get() = this.run.description override val scoreboards: List get() = TODO("Not yet implemented") From 1fdc4f71f8bb5473998edba9dab56acaf58aa448 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 5 Mar 2021 21:57:18 +0100 Subject: [PATCH 44/95] Added descriptions to the RunActionContext --- .../dev/dres/api/cli/CompetitionRunCommand.kt | 4 +-- .../dres/data/model/run/RunActionContext.kt | 25 +++++++++++++++---- .../run/InteractiveSynchronousRunManager.kt | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt index d2c047ca2..bf70522fb 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt @@ -53,7 +53,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktComm if (plain) { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { println("${RunSummary(it.id.string, it.name, it.competitionDescription.description, it.currentTaskDescription( - RunActionContext.DUMMY_ADMIN)?.name)} (${it.status})") + RunActionContext.INTERNAL)?.name)} (${it.status})") } } else { table { @@ -68,7 +68,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktComm body { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { row(it.id.string, it.name, it.competitionDescription.description, it.currentTaskDescription( - RunActionContext.DUMMY_ADMIN)?.name + RunActionContext.INTERNAL)?.name ?: "N/A", it.status) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index da5bb62a7..5c4cc67a5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -10,19 +10,34 @@ import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context +/** + * The [RunActionContext] captures and encapsulates information usually required during the interaction with a [RunManager]. + * It exposes information available to the OpenAPI facility (e.g., through session management) to the [RunManager]. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.0 + */ data class RunActionContext(val userId: UserId, val teamId: TeamId?, val roles: Set) { + + /** True if the user associated with this [RunActionContext] acts as [Role.ADMIN]*/ val isAdmin: Boolean - get() = roles.contains(Role.ADMIN) + get() = this.roles.contains(Role.ADMIN) companion object { + /** A static [RunActionContext] used for internal invocations by DRES. Always acts as an implicit [Role.ADMIN]. */ + val INTERNAL = RunActionContext(UID.EMPTY, UID.EMPTY, setOf(Role.ADMIN)) + + /** + * Constructs a [RunActionContext] from a [Context] and a [RunManager]. + * + * @param ctx The Javalin [Context] to construct the [RunActionContext] from. + * @param runManager The [RunManager] to construct the [RunActionContext] for. + */ fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user", ctx) + val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) val roles = AccessManager.rolesOfSession(ctx.sessionId()).map { Role.fromRestRole(it) }.toSet() val teamId = runManager.competitionDescription.teams.find { it.users.contains(userId) }?.uid - return RunActionContext(userId, teamId, roles) } - - val DUMMY_ADMIN = RunActionContext(UID.EMPTY, UID.EMPTY, setOf(Role.ADMIN)) } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index ecb38eba6..5596522c5 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -117,7 +117,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) /** The internal [DAOUpdatable] used to end a task once no more submissions are possible */ - private val endTaskUpdatable = EndTaskUpdatable(this, RunActionContext.DUMMY_ADMIN ) + private val endTaskUpdatable = EndTaskUpdatable(this, RunActionContext.INTERNAL ) /** List of [Updatable] held by this [InteractiveSynchronousRunManager]. */ private val updatables = mutableListOf() From bdd6f5b8b24c9762a0f6c64b62d0a8c3eebc8b5a Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sat, 6 Mar 2021 08:45:54 +0100 Subject: [PATCH 45/95] Some more refactoring. --- .../dev/dres/data/model/competition/Ids.kt | 9 +++++++ .../data/model/competition/TaskDescription.kt | 23 +++++++++------- .../dres/data/model/competition/TaskType.kt | 8 +++--- .../dev/dres/data/model/competition/Team.kt | 2 -- .../interfaces/SubmissionFilterFactory.kt | 20 ++++++++++++++ .../interfaces/TaskScorerFactory.kt | 19 +++++++++++++ .../run/InteractiveAsynchronousCompetition.kt | 4 +-- .../run/InteractiveSynchronousCompetition.kt | 4 +-- .../model/run/NonInteractiveCompetition.kt | 4 +-- .../dres/data/model/run/interfaces/Task.kt | 7 +++-- .../handlers/TeamCombinationScoreHandler.kt | 8 +++--- .../interfaces/IncrementalTaskRunScorer.kt | 21 --------------- .../score/interfaces/IncrementalTaskScorer.kt | 18 +++++++++++++ .../interfaces/RecalculatingTaskRunScorer.kt | 18 ------------- .../interfaces/RecalculatingTaskScorer.kt | 27 +++++++++++++++++++ .../{TaskRunScorer.kt => TaskScorer.kt} | 6 +++-- .../scoreboard/MaxNormalizingScoreBoard.kt | 4 +-- .../dres/run/score/scoreboard/Scoreboard.kt | 7 +++-- .../scoreboard/SumAggregateScoreBoard.kt | 5 ++-- .../dres/run/score/scorer/AvsTaskScorer.kt | 4 +-- .../dres/run/score/scorer/KisTaskScorer.kt | 4 +-- .../dres/run/updatables/ScoresUpdatable.kt | 11 ++++---- 22 files changed, 143 insertions(+), 90 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/TaskScorerFactory.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskRunScorer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskScorer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskRunScorer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskScorer.kt rename backend/src/main/kotlin/dev/dres/run/score/interfaces/{TaskRunScorer.kt => TaskScorer.kt} (61%) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt new file mode 100644 index 000000000..4f2f6d2f0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt @@ -0,0 +1,9 @@ +package dev.dres.data.model.competition + +import dev.dres.data.model.UID + +/** The ID of a [TaskDescription]. */ +typealias TaskDescriptionId = UID + +/** The ID of a [Team]. */ +typealias TeamId = UID \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt index dc0772d76..2e751556c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt @@ -7,20 +7,22 @@ import dev.dres.data.dbo.DAO import dev.dres.data.model.Config import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.competition.interfaces.SubmissionFilterFactory +import dev.dres.data.model.competition.interfaces.TaskScorerFactory +import dev.dres.data.model.run.interfaces.Task import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import java.io.FileNotFoundException import java.io.IOException import java.io.PrintStream import kotlin.math.max -typealias TaskDescriptionId = UID - /** - * Basic description of a [TaskDescription]. + * Basic description of a [Task] as executed in DRES. Defines basic attributes such as its name, its duration, + * the [TaskDescriptionTarget] and the [TaskDescriptionHint]s, that should be presented to the user. * - * @version 1.0.1 + * @version 1.0.2 * @author Luca Rossetto & Ralph Gasser */ class TaskDescription( @@ -48,15 +50,15 @@ class TaskDescription( /** List of [TaskDescriptionHint]s that act as clues to find the target media. */ val hints: List -){ +): TaskScorerFactory, SubmissionFilterFactory { /** - * Generates a new [TaskRunScorer] for this [TaskDescription]. Depending + * Generates a new [TaskScorer] for this [TaskDescription]. Depending * on the implementation, the returned instance is a new instance or being re-use. * - * @return [TaskRunScorer]. + * @return [TaskScorer]. */ - fun newScorer(): TaskRunScorer = taskType.newScorer() + override fun newScorer(): TaskScorer = taskType.newScorer() /** @@ -65,7 +67,7 @@ class TaskDescription( * * @return [SubmissionFilter] */ - fun newFilter(): SubmissionFilter = taskType.newFilter() + override fun newFilter(): SubmissionFilter = taskType.newFilter() /** * Generates and returns a [TaskHint] object to be used by the RESTful interface. @@ -123,6 +125,7 @@ class TaskDescription( /** * Checks if no components of the same type overlap + * * @throws IllegalArgumentException */ fun validate() { diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt index 919206cf5..c61fdea35 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt @@ -1,7 +1,7 @@ package dev.dres.data.model.competition import dev.dres.run.filter.* -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.KisTaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator @@ -58,12 +58,12 @@ data class TaskType( } /** - * Generates a new [TaskRunScorer] for this [TaskDescription]. Depending + * Generates a new [TaskScorer] for this [TaskDescription]. Depending * on the implementation, the returned instance is a new instance or being re-use. * - * @return [TaskRunScorer]. + * @return [TaskScorer]. */ - fun newScorer(): TaskRunScorer = when(score.option){ + fun newScorer(): TaskScorer = when(score.option){ ScoringType.KIS -> KisTaskScorer(score.parameters) ScoringType.AVS -> AvsTaskScorer() } diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt index 4e9dfbe65..4c7f59a35 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt @@ -6,8 +6,6 @@ import dev.dres.data.model.admin.UserId import java.nio.file.Path import java.nio.file.Paths -typealias TeamId = UID - /** * Represents a [Team] that takes part in a competition managed by DRES. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt new file mode 100644 index 000000000..4bfccefb0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt @@ -0,0 +1,20 @@ +package dev.dres.data.model.competition.interfaces + +import dev.dres.run.filter.SubmissionFilter + +/** + * A factory for [SubmissionFilter]s. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +interface SubmissionFilterFactory { + + /** + * Generates and returns a [SubmissionFilter]. Depending on the implementation, the returned instance + * is a new instance or can re-used. + * + * @return [SubmissionFilter] + */ + fun newFilter(): SubmissionFilter +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/TaskScorerFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/TaskScorerFactory.kt new file mode 100644 index 000000000..687050d3e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/TaskScorerFactory.kt @@ -0,0 +1,19 @@ +package dev.dres.data.model.competition.interfaces + +import dev.dres.run.score.interfaces.TaskScorer + +/** + * A factory for [TaskScorer]s. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +interface TaskScorerFactory { + /** + * Generates a new [TaskScorer]. Depending on the implementation, the returned instance + * is a new instance or being re-use. + * + * @return [TaskScorer]. + */ + fun newScorer(): TaskScorer +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index c66efd919..8c68302f6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -11,7 +11,7 @@ import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -79,7 +79,7 @@ class InteractiveAsynchronousCompetition(override var id: CompetitionId, overrid override val filter: SubmissionFilter = this.description.newFilter() @Transient - override val scorer: TaskRunScorer = this.description.newScorer() + override val scorer: TaskScorer = this.description.newScorer() @Transient override val validator: SubmissionValidator = this.newValidator() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt index d1092affa..4ebf9da46 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt @@ -12,7 +12,7 @@ import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import java.util.* @@ -77,7 +77,7 @@ class InteractiveSynchronousCompetition(override var id: CompetitionId, override override val filter: SubmissionFilter = description.newFilter() @Transient - override val scorer: TaskRunScorer = description.newScorer() + override val scorer: TaskScorer = description.newScorer() @Transient override val validator: SubmissionValidator = newValidator() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt index 8f491dda1..761bdeebf 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt @@ -10,7 +10,7 @@ import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.aspects.OriginAspect import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer class NonInteractiveCompetition(override var id: CompetitionId, override val name: String, override val description: CompetitionDescription): AbstractRun(), Competition { @@ -42,6 +42,6 @@ class NonInteractiveCompetition(override var id: CompetitionId, override val nam .find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") @Transient - override val scorer: TaskRunScorer = description.newScorer() + override val scorer: TaskScorer = description.newScorer() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt index d81492ada..2daf98696 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt @@ -2,8 +2,7 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task -import dev.dres.data.model.submissions.Submission -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer /** * Represents a [Task] solved by a DRES user or client. @@ -24,6 +23,6 @@ interface Task: Run { /** Reference to the [TaskDescription] describing this [Task]. */ val description: TaskDescription - /** The [TaskRunScorer] used to update score for this [Task]. */ - val scorer: TaskRunScorer + /** The [TaskScorer] used to update score for this [Task]. */ + val scorer: TaskScorer } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index 7fb16a58e..ba5b1ca85 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -4,8 +4,8 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* -import dev.dres.run.score.interfaces.IncrementalTaskRunScorer -import dev.dres.run.score.interfaces.RecalculatingTaskRunScorer +import dev.dres.run.score.interfaces.IncrementalTaskScorer +import dev.dres.run.score.interfaces.RecalculatingTaskScorer import java.io.File import java.io.PrintWriter @@ -60,7 +60,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { } when(scorer) { - is RecalculatingTaskRunScorer -> { + is RecalculatingTaskScorer -> { scorer.computeScores( combinedSubmissions, combinations.keys, @@ -69,7 +69,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { event.timeStamp ) } - is IncrementalTaskRunScorer -> { + is IncrementalTaskScorer -> { combinedSubmissions.forEach { scorer.update(it) } } else -> throw IllegalStateException("unsupported scorer type $scorer") diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskRunScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskRunScorer.kt deleted file mode 100644 index 549236155..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskRunScorer.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.dres.run.score.interfaces - -import dev.dres.data.model.submissions.Submission - -/** - * A [RecalculatingTaskRunScorer] that can update scores incrementally on a [Submission] by [Submission] basis. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0 - */ -interface IncrementalTaskRunScorer: TaskRunScorer { - /** - * Updates this [RecalculatingTaskRunScorer]'s score just using a single submission. Not all types of - * [RecalculatingTaskRunScorer]'s support incremental updating. If not, this method should throw an - * [UnsupportedOperationException]. - * - * @param submission The [Submission] to update this [RecalculatingTaskRunScorer] with. - */ - fun update(submission: Submission) - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskScorer.kt new file mode 100644 index 000000000..b5a7be812 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskScorer.kt @@ -0,0 +1,18 @@ +package dev.dres.run.score.interfaces + +import dev.dres.data.model.submissions.Submission + +/** + * A [TaskScorer] implementation that can update scores incrementally on a [Submission] by [Submission] basis. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.0 + */ +interface IncrementalTaskScorer: TaskScorer { + /** + * Updates this [IncrementalTaskScorer]'s score just using a single submission. + * + * @param submission The [Submission] to update this [IncrementalTaskScorer] with. + */ + fun update(submission: Submission) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskRunScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskRunScorer.kt deleted file mode 100644 index 2e782848f..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskRunScorer.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.dres.run.score.interfaces - -import dev.dres.data.model.UID -import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.InteractiveSynchronousCompetition -import dev.dres.data.model.submissions.Submission - -/** - * Computes the current scores of all teams for a given [InteractiveSynchronousCompetition.Task] - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.1 - */ -interface RecalculatingTaskRunScorer: TaskRunScorer { - - fun computeScores(submissions: Collection, teamIds: Collection, taskStartTime: Long, taskDuration: Long, taskEndTime: Long = 0): Map - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskScorer.kt new file mode 100644 index 000000000..6f19a6795 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskScorer.kt @@ -0,0 +1,27 @@ +package dev.dres.run.score.interfaces + +import dev.dres.data.model.UID +import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.submissions.Submission + +/** + * A [TaskScorer] implementation that re-computes the current scores of all teams for a given [Task] based on the + * entire [Submission] history. As opposed to the [IncrementalTaskScorer], incremental updates are not possible. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.2 + */ +interface RecalculatingTaskScorer: TaskScorer { + /** + * Re-computes this [RecalculatingTaskScorer]'s score based on the given [Submission] history. + * + * @param submissions The [Submission]s used to update this [RecalculatingTaskScorer] with. + * @param taskStartTime Time the [Task] started. + * @param taskDuration Duration of the [Task]. + * @param taskEndTime Time the [Task] ended. + * + * TODO: Should we maybe introduce a "TaskContext" here instead of handing over these individual parameters? + */ + fun computeScores(submissions: Collection, teamIds: Collection, taskStartTime: Long, taskDuration: Long, taskEndTime: Long = 0): Map +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskRunScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt similarity index 61% rename from backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskRunScorer.kt rename to backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt index 834ac6205..96d7f474a 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskRunScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt @@ -2,14 +2,16 @@ package dev.dres.run.score.interfaces import dev.dres.data.model.UID import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.run.interfaces.Task /** - * A scorer for a [TaskRunScorer] + * A scorer for a [Task]. A score is a [Double] value that captures a teams performance. + * The [TaskScorer] calculates and tracks these scores per [TeamId]. * * @version 1.1.0 * @author Luca Rossetto */ -interface TaskRunScorer { +interface TaskScorer { /** * Generates and returns the current scores for all teams in the relevant Task. * diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 61009fc50..f1bf2af24 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -5,7 +5,7 @@ import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.Team import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer import java.util.concurrent.ConcurrentHashMap import kotlin.math.max @@ -35,7 +35,7 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, pri override fun score(teamId: UID) = overallScoreMap()[teamId] ?: 0.0 - override fun update(scorers: Map) { + override fun update(scorers: Map) { this.scorePerTaskMap.clear() this.scorePerTaskMap.putAll(scorers.map { it.key to it.value.scores() }.toMap()) } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt index 643de19a5..b29db1c3f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt @@ -4,9 +4,8 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.Team import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.run.interfaces.TaskId -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer /** * Container for [Scoreboard]. @@ -47,9 +46,9 @@ interface Scoreboard { fun update(runs: List) /** - * Updates using a map of the [TaskRun] ids to the corresponding [TaskRunScorer]s + * Updates using a map of the [TaskRun] ids to the corresponding [TaskScorer]s */ - fun update(scorers: Map) + fun update(scorers: Map) /** * Returns a summary of all current scores in a [ScoreOverview] diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt index 4f4207b33..5290deaca 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt @@ -2,9 +2,8 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.run.interfaces.TaskId -import dev.dres.run.score.interfaces.TaskRunScorer +import dev.dres.run.score.interfaces.TaskScorer class SumAggregateScoreBoard(override val name: String, private val boards: List, private val taskGroupName: String? = null) : Scoreboard { @@ -19,7 +18,7 @@ class SumAggregateScoreBoard(override val name: String, private val boards: List //since calls are delegated, nothing needs to be done here } - override fun update(scorers: Map) { + override fun update(scorers: Map) { //since calls are delegated, nothing needs to be done here } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 9416652ce..c3ec13b95 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -5,13 +5,13 @@ import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.competition.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.run.score.interfaces.RecalculatingTaskRunScorer +import dev.dres.run.score.interfaces.RecalculatingTaskScorer import dev.dres.utilities.TimeUtil import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write -class AvsTaskScorer: RecalculatingTaskRunScorer { +class AvsTaskScorer: RecalculatingTaskScorer { private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index bb0ece364..bcafbb875 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -4,7 +4,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.run.score.interfaces.RecalculatingTaskRunScorer +import dev.dres.run.score.interfaces.RecalculatingTaskScorer import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write @@ -14,7 +14,7 @@ class KisTaskScorer( private val maxPointsPerTask: Double = defaultmaxPointsPerTask, private val maxPointsAtTaskEnd: Double = defaultmaxPointsAtTaskEnd, private val penaltyPerWrongSubmission: Double = defaultpenaltyPerWrongSubmission -) : RecalculatingTaskRunScorer { +) : RecalculatingTaskScorer { constructor(parameters: Map) : this( parameters.getOrDefault("maxPointsPerTask", "$defaultmaxPointsPerTask").toDoubleOrNull() ?: defaultmaxPointsPerTask, diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index 6c5cb3d32..aa48b3147 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -5,12 +5,11 @@ import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.InteractiveSynchronousCompetition -import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.RunManagerStatus -import dev.dres.run.score.interfaces.IncrementalTaskRunScorer -import dev.dres.run.score.interfaces.RecalculatingTaskRunScorer +import dev.dres.run.score.interfaces.IncrementalTaskScorer +import dev.dres.run.score.interfaces.RecalculatingTaskScorer import java.util.* /** @@ -37,13 +36,13 @@ class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdat override fun update(status: RunManagerStatus) { if (!this.list.isEmpty()) { - val scorersToUpdate = mutableSetOf>() + val scorersToUpdate = mutableSetOf>() val removed = this.list.removeIf { val scorer = it.first.scorer if (it.second.status != SubmissionStatus.INDETERMINATE) { when(scorer) { - is RecalculatingTaskRunScorer -> scorersToUpdate.add(Pair(it.first, scorer)) - is IncrementalTaskRunScorer -> scorer.update(it.second) + is RecalculatingTaskScorer -> scorersToUpdate.add(Pair(it.first, scorer)) + is IncrementalTaskScorer -> scorer.update(it.second) else -> { } } true From 1ce3b7c22f74e0f3adc7132082aa26610600004f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sat, 6 Mar 2021 10:02:13 +0100 Subject: [PATCH 46/95] More refactoring + continued implementation of InteractiveAsynchronousRunManager.kt --- .../dev/dres/api/cli/CompetitionRunCommand.kt | 4 +- .../kotlin/dev/dres/api/rest/AccessManager.kt | 2 +- .../rest/handler/BatchSubmissionHandler.kt | 2 +- .../handler/CompetitionRunAdminHandler.kt | 2 +- .../CompetitionRunClientInfoHandler.kt | 6 +- .../api/rest/handler/CompetitionRunHandler.kt | 20 +- .../handler/CompetitionRunScoreHandler.kt | 10 +- .../dev/dres/api/rest/handler/LogHandler.kt | 2 +- .../api/rest/handler/SubmissionHandler.kt | 4 +- .../dev/dres/api/rest/types/run/RunInfo.kt | 10 +- .../run/InteractiveAsynchronousCompetition.kt | 21 +- .../dres/data/model/run/RunActionContext.kt | 2 +- .../run/InteractiveAsynchronousRunManager.kt | 460 +++++++++++++++--- .../dev/dres/run/InteractiveRunManager.kt | 16 +- .../run/InteractiveSynchronousRunManager.kt | 35 +- .../dev/dres/run/NonInteractiveRunManager.kt | 4 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 20 +- .../main/kotlin/dev/dres/run/RunManager.kt | 2 +- .../dres/run/updatables/EndTaskUpdatable.kt | 2 +- 19 files changed, 487 insertions(+), 137 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt index bf70522fb..05777bde9 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt @@ -52,7 +52,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktComm } if (plain) { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - println("${RunSummary(it.id.string, it.name, it.competitionDescription.description, it.currentTaskDescription( + println("${RunSummary(it.id.string, it.name, it.description.description, it.currentTaskDescription( RunActionContext.INTERNAL)?.name)} (${it.status})") } } else { @@ -67,7 +67,7 @@ class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktComm } body { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - row(it.id.string, it.name, it.competitionDescription.description, it.currentTaskDescription( + row(it.id.string, it.name, it.description.description, it.currentTaskDescription( RunActionContext.INTERNAL)?.name ?: "N/A", it.status) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 1bd69e84d..35875f653 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -64,7 +64,7 @@ object AccessManager { * @param runManager The [RunManager] to register. */ fun registerRunManager(runManager: RunManager) { - runManager.competitionDescription.teams.flatMap { t -> t.users }.forEach { + runManager.description.teams.flatMap { t -> t.users }.forEach { if (this.usersToRunMap.containsKey(it)) { this.usersToRunMap[it]?.add(runManager) } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt index f421082aa..7cba9bb86 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt @@ -77,7 +77,7 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D throw ErrorStatusException(400, "Error parsing json batch", ctx) } - val team = runManager.competitionDescription.teams.find { + val team = runManager.description.teams.find { it.users.contains(userId) }?.uid ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index 2616a27c3..648f341f6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -84,7 +84,7 @@ class CreateCompetitionRunAdminHandler(private val competitions: DAO { val userId = userId(ctx) - return RunExecutor.managers().filter { m -> m.competitionDescription.teams.any { it.users.contains(userId) } } + return RunExecutor.managers().filter { m -> m.description.teams.any { it.users.contains(userId) } } } fun getRun(ctx: Context, runId: UID): RunManager? { val userId = userId(ctx) val run = RunExecutor.managerForId(runId) ?: return null - if (run.competitionDescription.teams.any { it.users.contains(userId) }) { + if (run.description.teams.any { it.users.contains(userId) }) { return run } return null @@ -57,7 +57,7 @@ data class ClientRunInfo( constructor(runManager: RunManager) : this( runManager.id.string, runManager.name, - runManager.competitionDescription.description, + runManager.description.description, runManager.status ) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index 85b40ecf2..056095703 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -46,7 +46,7 @@ abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRes fun getRelevantManagers(ctx: Context): List { if (isParticipant(ctx)) { val userId = userId(ctx) - return RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).filter { m -> m.competitionDescription.teams.any { it.users.contains(userId) } } + return RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).filter { m -> m.description.teams.any { it.users.contains(userId) } } } return RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java) } @@ -55,7 +55,7 @@ abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRes if (isParticipant(ctx)) { val userId = userId(ctx) val run = RunExecutor.managerForId(runId) ?: return null - if (run is InteractiveRunManager && run.competitionDescription.teams.any { it.users.contains(userId) }) { + if (run is InteractiveRunManager && run.description.teams.any { it.users.contains(userId) }) { return run } return null @@ -133,7 +133,7 @@ class GetCompetitionRunInfoHandler : AbstractCompetitionRunRestHandler(), GetRes val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ + if (!run.description.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access Denied", ctx) } @@ -161,7 +161,7 @@ class GetCompetitionRunStateHandler : AbstractCompetitionRunRestHandler(), GetRe val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ + if (!run.description.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access Denied", ctx) } @@ -194,7 +194,7 @@ class CurrentTaskInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandl val rac = runActionContext(ctx, run) - if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ + if (!run.description.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied.", ctx) } @@ -222,7 +222,7 @@ class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRu val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ + if (!run.description.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied.", ctx) } @@ -260,7 +260,7 @@ class CurrentTaskTargetHandler(private val config: Config, private val collectio val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) /* Test for access rights. */ - if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ + if (!run.description.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied.", ctx) } @@ -309,7 +309,7 @@ class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandle val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) val rac = runActionContext(ctx, run) - if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ + if (!run.description.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied.", ctx) } @@ -348,7 +348,7 @@ class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRest val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) val rac = runActionContext(ctx, run) - if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ + if (!run.description.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied", ctx) } @@ -390,7 +390,7 @@ class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRes val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) val rac = runActionContext(ctx, run) - if (!run.competitionDescription.participantCanView && isParticipant(ctx)){ + if (!run.description.participantCanView && isParticipant(ctx)){ throw ErrorStatusException(403, "Access denied", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index 76fe64804..ef2f2498a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -44,7 +44,7 @@ abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler if (isParticipant(ctx)) { val userId = userId(ctx) val run = RunExecutor.managerForId(runId) ?: return null - if (run is InteractiveRunManager && run.competitionDescription.teams.any { it.users.contains(userId) }) { + if (run is InteractiveRunManager && run.description.teams.any { it.users.contains(userId) }) { return run } return null @@ -112,14 +112,14 @@ class CurrentTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler + run.description.teams.map { team -> Score(team.uid.string, scores[team.uid] ?: 0.0) } ) @@ -169,7 +169,7 @@ class HistoryTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler - task.scorer.scores().map { "${task.description.name},${task.description.taskGroup.name},${run.competitionDescription.teams.find { t -> t.uid == it.key }?.name ?: "???"},${it.value}" } + task.scorer.scores().map { "${task.description.name},${task.description.taskGroup.name},${run.description.teams.find { t -> t.uid == it.key }?.name ?: "???"},${it.value}" } }.joinToString(separator = "\n") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt index 6a7208f25..5427377e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt @@ -31,7 +31,7 @@ abstract class LogHandler : PostRestHandler, AccessManagedRestHan } if (managers.size > 1) { - throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.competitionDescription.name }}", ctx) + throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.description.name }}", ctx) } return managers.first() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index e8c8d2da9..7eb118b2c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -60,7 +60,7 @@ class SubmissionHandler (val collections: DAO, private val item } if (managers.size > 1) { - throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.competitionDescription.name }}", ctx) + throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.description.name }}", ctx) } return managers.first() @@ -70,7 +70,7 @@ class SubmissionHandler (val collections: DAO, private val item val map = ctx.queryParamMap() /* Find team that the user belongs to. */ - val team = runManager.competitionDescription.teams.find { + val team = runManager.description.teams.find { it.users.contains(userId) }?.uid ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt index 46b5b16a4..ffe14e956 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt @@ -22,10 +22,10 @@ data class RunInfo( constructor(run: RunManager) : this( run.id.string, run.name, - run.competitionDescription.description, - run.competitionDescription.teams.map { TeamInfo(it) }, - run.competitionDescription.tasks.map { TaskInfo(it) }, - run.competitionDescription.id.string, - run.competitionDescription.participantCanView + run.description.description, + run.description.teams.map { TeamInfo(it) }, + run.description.tasks.map { TaskInfo(it) }, + run.description.id.string, + run.description.participantCanView ) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index 8c68302f6..9489dbbdd 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -26,11 +26,6 @@ import java.util.concurrent.ConcurrentHashMap */ class InteractiveAsynchronousCompetition(override var id: CompetitionId, override val name: String, override val description: CompetitionDescription): AbstractRun(), Competition { - init { - require(description.tasks.size > 0) { "Cannot create a run from a competition that doesn't have any tasks. "} - require(description.teams.size > 0) { "Cannot create a run from a competition that doesn't have any teams. "} - } - /** A [ConcurrentHashMap] that maps a list of [Task]s to the [TeamId]s they belong to.*/ private val tasksMap = ConcurrentHashMap>() @@ -38,12 +33,26 @@ class InteractiveAsynchronousCompetition(override var id: CompetitionId, overrid override val tasks: List get() = this.tasksMap.values.flatten() + init { + require(description.tasks.size > 0) { "Cannot create a run from a competition that doesn't have any tasks. "} + require(description.teams.size > 0) { "Cannot create a run from a competition that doesn't have any teams. "} + this.description.teams.forEach { this.tasksMap[it.uid] = LinkedList() } + } + /** * Returns the current [Task] for the given [TeamId]. * * @param teamId The [TeamId] to lookup. */ - fun currentTaskForTeam(teamId: TeamId): Task? = this.tasksMap[teamId]?.last() + fun currentTaskForTeam(teamId: TeamId): Task? = this.tasksForTeam(teamId).lastOrNull() + + /** + * Returns all [Task]s for the given [TeamId]. + * + * @param teamId The [TeamId] to lookup. + * @return List [] + */ + fun tasksForTeam(teamId: TeamId) = this.tasksMap[teamId] ?: throw IllegalArgumentException("Given $teamId is unknown to this competition $id.") /** * Generates and returns a [String] representation for this [InteractiveAsynchronousCompetition]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 5c4cc67a5..32270d8ef 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -36,7 +36,7 @@ data class RunActionContext(val userId: UserId, val teamId: TeamId?, val roles: fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) val roles = AccessManager.rolesOfSession(ctx.sessionId()).map { Role.fromRestRole(it) }.toSet() - val teamId = runManager.competitionDescription.teams.find { it.users.contains(userId) }?.uid + val teamId = runManager.description.teams.find { it.users.contains(userId) }?.uid return RunActionContext(userId, teamId, roles) } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 81b381a2d..e535784ba 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -2,6 +2,7 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.run.websocket.ClientMessage +import dev.dres.api.rest.types.run.websocket.ClientMessageType import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID @@ -9,16 +10,21 @@ import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.* +import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.LogEventSource +import dev.dres.run.eventstream.EventStreamProcessor +import dev.dres.run.eventstream.TaskEndEvent import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard -import dev.dres.run.updatables.DAOUpdatable -import dev.dres.run.updatables.MessageQueueUpdatable -import dev.dres.run.updatables.ScoreboardsUpdatable -import dev.dres.run.updatables.ScoresUpdatable +import dev.dres.run.updatables.* import dev.dres.run.validation.interfaces.JudgementValidator +import dev.dres.utilities.extensions.UID import org.slf4j.LoggerFactory +import java.util.* +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.collections.HashMap import kotlin.concurrent.read @@ -40,7 +46,8 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous companion object { private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousRunManager::class.java) - private val SCOREBOARD_UPDATE_INTERVAL_MS = 1000L // TODO make configurable + private const val SCOREBOARD_UPDATE_INTERVAL_MS = 1000L // TODO make configurable + private const val MAXIMUM_ERROR_COUNT = 5 } /** Tracks the current [TaskDescription] per [TeamId]. */ @@ -49,11 +56,14 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous /** Tracks the current [TaskDescription] per [TeamId]. */ private val statusMap: MutableMap = HashMap() + /** A [Map] of all viewers, i.e., DRES clienst currently registered with this [InteractiveAsynchronousRunManager]. */ + private val viewers = ConcurrentHashMap() + /** A lock for state changes to this [InteractiveAsynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.competitionDescription.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.description.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) @@ -64,6 +74,12 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous /** The internal [DAOUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) + /** The internal [DAOUpdatable] used to end a task once no more submissions are possible */ + private val endTaskUpdatable = EndTaskUpdatable(this, RunActionContext.INTERNAL) + + /** List of [Updatable] held by this [InteractiveAsynchronousRunManager]. */ + private val updatables = mutableListOf() + /** Run ID of this [InteractiveAsynchronousCompetition]. */ override val id: UID get() = this.run.id @@ -73,31 +89,130 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous get() = this.run.name /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ - override val competitionDescription: CompetitionDescription + override val description: CompetitionDescription get() = this.run.description - override fun currentTaskDescription(context: RunActionContext): TaskDescription { - require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager."} - return this.navigationMap[context.teamId] ?: throw IllegalStateException("Could not find active task description for team ${context.teamId}.") - } + /** The global [RunManagerStatus] of this [InteractiveAsynchronousRunManager]. */ + @Volatile + override var status: RunManagerStatus = if (this.run.hasStarted) { RunManagerStatus.ACTIVE } else { RunManagerStatus.CREATED } + private set + + override val judgementValidators: List + get() = this.run.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } + + override val scoreboards: List + get() = this.scoreboardsUpdatable.scoreboards override val scoreHistory: List - get() = TODO("Not yet implemented") + get() = this.scoreboardsUpdatable.timeSeries override val allSubmissions: List - get() = TODO("Not yet implemented") + get() = this.stateLock.read { this.run.tasks.flatMap { it.submissions } } init { + /* Register relevant Updatables. */ + this.updatables.add(this.scoresUpdatable) + this.updatables.add(this.scoreboardsUpdatable) + this.updatables.add(this.messageQueueUpdatable) + this.updatables.add(this.daoUpdatable) + this.updatables.add(this.endTaskUpdatable) + /* Initialize map and set all tasks pointers to the first task. */ - this.competitionDescription.teams.forEach { - this.navigationMap[it.uid] = this.competitionDescription.tasks[0] + this.description.teams.forEach { + this.navigationMap[it.uid] = this.description.tasks[0] this.statusMap[it.uid] = if (this.run.hasStarted) { RunManagerStatus.ACTIVE } else { RunManagerStatus.CREATED } + + /** End ongoing runs upon initialization (in case server crashed during task execution). */ + if (this.run.tasksForTeam(it.uid).last().isRunning) { + this.run.tasksForTeam(it.uid).last().end() + } + } + + /** Re-enqueue pending submissions for judgement (if any). */ + this.run.tasks.forEach { run -> + run.submissions.filter { it.status == SubmissionStatus.INDETERMINATE }.forEach { + run.validator.validate(it) + } + } + + /** Re-calculate all the relevant scores. */ + this.run.tasks.forEach { run -> + run.submissions.forEach { sub -> + this.scoresUpdatable.enqueue(Pair(run, sub)) + } } + this.scoresUpdatable.update(this.status) } + /** + * Starts this [InteractiveAsynchronousCompetition] moving [RunManager.status] from [RunManagerStatus.CREATED] to + * [RunManagerStatus.ACTIVE] for all teams. This can only be executed by an administrator. + * + * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. + * + * @param context The [RunActionContext] for this invocation. + * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.CREATED] + */ + override fun start(context: RunActionContext) = this.stateLock.write { + check(this.status == RunManagerStatus.CREATED) { "SynchronizedRunManager is in status ${this.status} and cannot be started." } + if (context.isAdmin) { + /* Start the run. */ + this.run.start() - override fun tasks(context: RunActionContext): List { - TODO("Not yet implemented") + /* Update status. */ + this.statusMap.forEach { (t, _) -> this.statusMap[t] = RunManagerStatus.ACTIVE } + this.status = RunManagerStatus.ACTIVE + + /* Mark DAO for update. */ + this.daoUpdatable.dirty = true + + /* Enqueue WS message for sending */ + this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.COMPETITION_START)) + + LOGGER.info("Run manager ${this.id} started") + } + } + + /** + * Ends this [InteractiveAsynchronousCompetition] moving [RunManager.status] from [RunManagerStatus.ACTIVE] to + * [RunManagerStatus.TERMINATED] for all teams. This can only be executed by an administrator. + * + * As all state affecting methods, this method throws an [IllegalStateException] if invocation + * does not match the current state. + * + * @param context The [RunActionContext] for this invocation. + * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] + */ + override fun end(context: RunActionContext) { + check(this.status != RunManagerStatus.TERMINATED) { "SynchronizedRunManager is in status ${this.status} and cannot be terminated." } + if (context.isAdmin) { + /* End the run. */ + this.run.end() + + /* Update status. */ + this.statusMap.forEach { (t, _) -> this.statusMap[t] = RunManagerStatus.TERMINATED } + this.status = RunManagerStatus.TERMINATED + + /* Mark DAO for update. */ + this.daoUpdatable.dirty = true + + /* Enqueue WS message for sending */ + this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.COMPETITION_END)) + + LOGGER.info("SynchronousRunManager ${this.id} terminated") + } + } + + /** + * Returns the currently active [TaskDescription] for the given team. Requires [RunManager.status] for the requesting team + * to be [RunManagerStatus.ACTIVE]. + * + * @param context The [RunActionContext] used for the invocation. + * @return The [TaskDescription] for the given team. + */ + override fun currentTaskDescription(context: RunActionContext): TaskDescription { + require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager."} + return this.navigationMap[context.teamId] ?: throw IllegalStateException("Could not find active task description for team ${context.teamId}.") } /** @@ -113,7 +228,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun previous(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.competitionDescription.tasks.indexOf(this.currentTaskDescription(context)) + 1 + val newIndex = this.description.tasks.indexOf(this.currentTaskDescription(context)) + 1 return try { this.goTo(context, newIndex) true @@ -134,7 +249,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun next(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.competitionDescription.tasks.indexOf(this.currentTaskDescription(context)) + 1 + val newIndex = this.description.tasks.indexOf(this.currentTaskDescription(context)) + 1 return try { this.goTo(context, newIndex) true @@ -157,10 +272,10 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous override fun goTo(context: RunActionContext, index: Int) = this.stateLock.write { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} check(this.statusMap[context.teamId] == RunManagerStatus.ACTIVE || this.status == RunManagerStatus.TASK_ENDED) { "Run manager for team ${context.teamId} is in status ${this.status}. Tasks can therefore not be changed." } - if (index >= 0 && index < this.competitionDescription.tasks.size) { + if (index >= 0 && index < this.description.tasks.size) { /* Update active task. */ - this.navigationMap[context.teamId] = this.competitionDescription.tasks[index] + this.navigationMap[context.teamId] = this.description.tasks[index] this.statusMap[context.teamId] = RunManagerStatus.ACTIVE /* Mark scoreboards for update. */ @@ -175,30 +290,74 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous } } - + /** + * Starts the [currentTask] for the given team and thus moves the [InteractiveAsynchronousRunManager.status] from [RunManagerStatus.ACTIVE] to + * either [RunManagerStatus.PREPARING_TASK] or [RunManagerStatus.RUNNING_TASK] + * + * TODO: Should a team be able to start the same task multiple times? How would this affect scoring? + * + * As all state affecting methods, this method throws an [IllegalStateException] if invocation oes not match the current state. + * + * @param context The [RunActionContext] used for the invocation. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] or [currentTask] is not set. + */ override fun startTask(context: RunActionContext) = this.stateLock.write { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} check(this.statusMap[context.teamId] == RunManagerStatus.ACTIVE) { "Run manager for team ${context.teamId} is in status ${this.status}. New task cannot be started." } val currentTask = this.navigationMap[context.teamId] check(currentTask != null) { } - /* Create and prepare pipeline for submission. */ + /* Create task and update status. */ this.run.Task(teamId = context.teamId, descriptionId = this.navigationMap[context.teamId]!!.id) - - /* Update status. */ this.statusMap[context.teamId] = RunManagerStatus.PREPARING_TASK /* Mark scoreboards and DAO for update. */ this.scoreboardsUpdatable.dirty = true this.daoUpdatable.dirty = true - LOGGER.info("SynchronousRunManager ${this.id} started task task $currentTask.") + LOGGER.info("Run manager ${this.id} started task $currentTask.") } + /** + * Force-abort the [currentTask] and thus moves the [InteractiveAsynchronousRunManager.status] for the given team from + * [RunManagerStatus.PREPARING_TASK] or [RunManagerStatus.RUNNING_TASK] to [RunManagerStatus.ACTIVE] + * + * TODO: Do we want users to be able to do this? If yes, I'd argue that the ability to repeat a task is a requirement. + * + * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. + * + * @param context The [RunActionContext] used for the invocation. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + */ override fun abortTask(context: RunActionContext) = this.stateLock.write { - TODO("Not yet implemented") + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} + check((this.statusMap[context.teamId] == RunManagerStatus.PREPARING_TASK || this.statusMap[context.teamId] == RunManagerStatus.RUNNING_TASK)) { + "Run manager for team ${context.teamId} is in status ${this.status}. New task cannot be started." + } + + /* End TaskRun and update status. */ + val currentTask = this.currentTask(context) ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") + currentTask.end() + this.statusMap[context.teamId] = RunManagerStatus.TASK_ENDED + + /* Mark scoreboards and DAO for update. */ + this.scoreboardsUpdatable.dirty = true + this.daoUpdatable.dirty = true + + /* Enqueue WS message for sending */ + this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_END)) + + LOGGER.info("Run manager ${this.id} aborted task $currentTask.") } + /** + * Returns the time in milliseconds that is left until the end of the currently running task for the given team. + * Only works if the [InteractiveAsynchronousRunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, + * this method returns -1L. + * + * @param context The [RunActionContext] used for the invocation. + * @return Time remaining until the task will end or -1, if no task is running. + */ override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} if (this.statusMap[context.teamId] == RunManagerStatus.RUNNING_TASK) { @@ -209,21 +368,68 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous } } - override fun currentTask(context: RunActionContext): AbstractInteractiveTask? { - TODO("Not yet implemented") + /** + * Returns the number of [AbstractInteractiveTask]s for this [InteractiveAsynchronousRunManager]. + * + * Depending on the [RunActionContext], the number may vary. + * + * @param context The [RunActionContext] used for the invocation. + * @return [List] of [AbstractInteractiveTask]s + */ + override fun taskCount(context: RunActionContext): Int { + if (context.isAdmin) return this.run.tasks.size + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} + return this.run.tasksForTeam(context.teamId).size } - override fun taskForId( - context: RunActionContext, - taskId: UID - ): InteractiveSynchronousCompetition.Task? { - TODO("Not yet implemented") + /** + * Returns a [List] of all [AbstractInteractiveTask]s for this [InteractiveAsynchronousRunManager]. + * + * Depending on the [RunActionContext], the list may vary. + * + * @param context The [RunActionContext] used for the invocation. + * @return [List] of [AbstractInteractiveTask]s + */ + override fun tasks(context: RunActionContext): List { + if (context.isAdmin) return this.run.tasks + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} + return this.run.tasksForTeam(context.teamId) } - override fun submissions(context: RunActionContext): List { - TODO("Not yet implemented") + /** + * Returns [AbstractInteractiveTask]s for a specific task [UID]. May be empty. + * + * @param context The [RunActionContext] used for the invocation. + * @param taskId The [UID] of the [AbstractInteractiveTask]. + */ + override fun taskForId(context: RunActionContext, taskId: UID): AbstractInteractiveTask? = this.tasks(context).find { it.uid == taskId } + + /** + * Returns a reference to the currently active [AbstractInteractiveTask]. + * + * @param context The [RunActionContext] used for the invocation. + * @return [AbstractInteractiveTask] that is currently active or null, if no such task is active. + */ + override fun currentTask(context: RunActionContext): AbstractInteractiveTask? = this.stateLock.read { + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} + return this.run.currentTaskForTeam(context.teamId) } + /** + * Returns the [Submission]s for all currently active [AbstractInteractiveTask]s or an empty [List], if no such task is active. + * + * @param context The [RunActionContext] used for the invocation. + * @return List of [Submission]s for the currently active [AbstractInteractiveTask] + */ + override fun submissions(context: RunActionContext): List = this.currentTask(context)?.submissions ?: emptyList() + + /** + * Adjusting task durations is not supported by the [InteractiveAsynchronousRunManager]s. + * + * @return Time left (see [timeLeft]). + */ + override fun adjustDuration(context: RunActionContext, s: Int): Long = this.timeLeft(context) + /** * Overriding the ready state is not supported by the [InteractiveAsynchronousRunManager]s. * @@ -233,52 +439,180 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous return false } - override fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus { - TODO("Not yet implemented") - } + /** + * Invoked by an external caller to post a new [Submission] for the [Task] that is currently being + * executed by this [InteractiveAsynchronousRunManager]. [Submission]s usually cause updates to the + * internal state and/or the [Scoreboard] of this [InteractiveRunManager]. + * + * This method will not throw an exception and instead returns false if a [Submission] was + * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke + * this method again. + * + * @param context The [RunActionContext] used for the invocation + * @param sub The [Submission] to be posted. + * + * @return [SubmissionStatus] of the [Submission] + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + */ + override fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus = this.stateLock.read { + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } + check(this.statusMap[context.teamId] == RunManagerStatus.RUNNING_TASK) { "Run manager is in status ${this.status} and can currently not accept submissions." } + + /* Register submission. */ + val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") + task.addSubmission(sub) + + /* Mark dao for update. */ + this.daoUpdatable.dirty = true - override fun updateSubmission(context: RunActionContext, submissionId: UID, submissionStatus: SubmissionStatus): Boolean { - TODO("Not yet implemented") + /* Enqueue submission for post-processing. */ + this.scoresUpdatable.enqueue(Pair(task, sub)) + + /* Enqueue WS message for sending */ + this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_UPDATED)) + + return sub.status } /** - * Adjusting task durations is not supported by the [InteractiveAsynchronousRunManager]s. + * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [SubmissionStatus]. + * [Submission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveAsynchronousRunManager]. * - * @return Time left (see [timeLeft]). + * This method will not throw an exception and instead returns false if a [Submission] was + * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke + * this method again. + * + * @param context The [RunActionContext] used for the invocation + * @param submissionId The [UID] of the [Submission] to update. + * @param submissionStatus The new [SubmissionStatus] + * + * @return Whether the update was successful or not + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - override fun adjustDuration(context: RunActionContext, s: Int): Long = this.timeLeft(context) + override fun updateSubmission(context: RunActionContext, submissionId: UID, submissionStatus: SubmissionStatus): Boolean = this.stateLock.read { + /* Sanity check. TODO: Do we indeed only want to be able to update submissions for the current task? */ + val found = this.submissions(context).find { it.uid == submissionId} ?: return false + /* Actual update - currently, only status update is allowed */ + if (found.status != submissionStatus) { + found.status = submissionStatus - override val scoreboards: List - get() = TODO("Not yet implemented") + /* Mark DAO for update. */ + this.daoUpdatable.dirty = true - override val status: RunManagerStatus - get() = TODO("Not yet implemented") + /* Enqueue submission for post-processing. */ + this.scoresUpdatable.enqueue(Pair(found.task!!, found)) - override val judgementValidators: List - get() = TODO("Not yet implemented") + /* Enqueue WS message for sending */ + this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_UPDATED)) - override fun start(context: RunActionContext) { - TODO("Not yet implemented") - } + return true + } - override fun end(context: RunActionContext) { - TODO("Not yet implemented") + return false } - override fun taskCount(context: RunActionContext): Int { - TODO("Not yet implemented") + /** + * Returns a list of viewer [WebSocketConnection]s for this [InteractiveAsynchronousRunManager] alongside with their respective ready + * state, which is always true for [InteractiveAsynchronousRunManager] + * + * @return List of viewer [WebSocketConnection]s for this [RunManager]. + */ + override fun viewers(): Map = Collections.unmodifiableMap(this.viewers) + + /** + * Processes WebSocket [ClientMessage] received by the [InteractiveAsynchronousRunManager]. + * + * @param connection The [WebSocketConnection] through which the message was received. + * @param message The [ClientMessage] received. + */ + override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean { + when (message.type) { + ClientMessageType.REGISTER -> this.viewers[connection] = true + ClientMessageType.UNREGISTER -> this.viewers.remove(connection) + else -> { /* No op. */} + } + return true } - override fun viewers(): Map { - TODO("Not yet implemented") + override fun run() { + /** Sort list of by [Phase] in ascending order. */ + this.updatables.sortBy { it.phase } + + /** Start [InteractiveSynchronousRunManager] . */ + var errorCounter = 0 + while (this.status != RunManagerStatus.TERMINATED) { + try { + /* 1) Invoke all relevant [Updatable]s. */ + this.invokeUpdatables() + + /* 2) Process internal state updates (if necessary). */ + this.internalStateUpdate() + + /* 3) Reset error counter and yield to other threads. */ + errorCounter = 0 + Thread.sleep(10) + } catch (ie: InterruptedException) { + LOGGER.info("Interrupted run manager thread; exiting...") + return + } catch (e: Throwable) { + LOGGER.error("Uncaught exception in run loop for competition run ${this.id}. Loop will continue to work but this error should be handled!", e) + + // oh shit, something went horribly horribly wrong + if (errorCounter >= MAXIMUM_ERROR_COUNT) { + LOGGER.error("Reached maximum consecutive error count of $MAXIMUM_ERROR_COUNT; terminating loop...") + RunExecutor.dump(this.run) + break + } else { + LOGGER.error("This is the ${++errorCounter}-th in a row. Run manager will terminate loop after $MAXIMUM_ERROR_COUNT errors") + } + } + } + + /** Invoke [Updatable]s one last time. */ + this.invokeUpdatables() + LOGGER.info("Run manager ${this.id} has reached end of run logic.") } - override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean { - TODO("Not yet implemented") + /** + * Invokes all [Updatable]s registered with this [InteractiveSynchronousRunManager]. + */ + private fun invokeUpdatables() = this.stateLock.read { + this.updatables.forEach { + if (it.shouldBeUpdated(this.status)) { /* TODO: This mechanism needs checking, since the status may be different for different teams. */ + try { + it.update(this.status) + } catch (e: Throwable) { + LOGGER.error("Uncaught exception while updating ${it.javaClass.simpleName} for competition run ${this.id}. Loop will continue to work but this error should be handled!", e) + } + } + } } - override fun run() { - TODO("Not yet implemented") + /** + * This is an internal method that facilitates internal state updates to this [InteractiveSynchronousRunManager], + * i.e., status updates that are not triggered by an outside interaction. + */ + private fun internalStateUpdate() = this.stateLock.read { + for (teamId in this.run.description.teams.map { it.uid }) { + if (this.statusMap[teamId] == RunManagerStatus.RUNNING_TASK) { + val task = this.run.currentTaskForTeam(teamId) ?: throw IllegalStateException("Could not find active task for team ${teamId} despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") + val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!)) + if (timeLeft <= 0) { + this.stateLock.write { + task.end() + this.statusMap[teamId] = RunManagerStatus.TASK_ENDED + AuditLogger.taskEnd(this.id, task.description.name, LogEventSource.INTERNAL, null) + EventStreamProcessor.event(TaskEndEvent(this.id, task.uid)) + } + + /* Mark DAO for update. */ + this.daoUpdatable.dirty = true + + /* Enqueue WS message for sending */ + this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_END)) + } + } + } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 321a736d6..76ff81af4 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -18,13 +18,6 @@ interface InteractiveRunManager : RunManager { /** List of all [Submission]s for this [InteractiveRunManager], irrespective of the [InteractiveSynchronousCompetition.Task] it belongs to. */ val allSubmissions: List - /** - * - * @param context The [RunActionContext] used for the invocation. - * @return [List] of [AbstractInteractiveTask]s - */ - override fun tasks(context: RunActionContext): List - /** * Prepares this [InteractiveRunManager] for the execution of previous [TaskDescription] as per order defined in * [CompetitionDescription.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. @@ -117,6 +110,15 @@ interface InteractiveRunManager : RunManager { */ fun timeLeft(context: RunActionContext): Long + /** + * Returns a list of all [AbstractInteractiveTask]s for this [InteractiveRunManager]. Depending on the + * implementation, that list may be filtered depending on the [RunActionContext]. + * + * @param context The [RunActionContext] used for the invocation. + * @return [List] of [AbstractInteractiveTask]s + */ + override fun tasks(context: RunActionContext): List + /** * Returns a reference to the currently active [AbstractInteractiveTask]. * diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 5596522c5..8d35bc4c0 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -62,14 +62,11 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio get() = this.run.name /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ - override val competitionDescription: CompetitionDescription + override val description: CompetitionDescription get() = this.run.description /** Reference to the currently active [TaskDescription]. This is part of the task navigation. */ - private var currentTaskDescription = this.competitionDescription.tasks[0] - - /** List of [InteractiveSynchronousCompetition.Task] for this [InteractiveSynchronousRunManager]. */ - override fun tasks(context: RunActionContext): List = this.run.tasks + private var currentTaskDescription = this.description.tasks[0] /** The list of all [Submission]s tracked ever received by this [InteractiveSynchronousRunManager]. */ override val allSubmissions: List @@ -105,7 +102,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio private val readyLatch = ReadyLatch() /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.competitionDescription.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.description.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) @@ -206,7 +203,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio override fun previous(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.competitionDescription.tasks.indexOf(this.currentTaskDescription) - 1 + val newIndex = this.description.tasks.indexOf(this.currentTaskDescription) - 1 return try { this.goToTask(newIndex) true @@ -217,7 +214,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio override fun next(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.competitionDescription.tasks.indexOf(this.currentTaskDescription) + 1 + val newIndex = this.description.tasks.indexOf(this.currentTaskDescription) + 1 return try { this.goToTask(newIndex) true @@ -233,10 +230,10 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio private fun goToTask(index: Int) = this.stateLock.write { check(this.status == RunManagerStatus.ACTIVE || this.status == RunManagerStatus.TASK_ENDED) { "SynchronizedRunManager is in status ${this.status}. Tasks can therefore not be changed." } - if (index >= 0 && index < this.competitionDescription.tasks.size) { + if (index >= 0 && index < this.description.tasks.size) { /* Update active task. */ - this.currentTaskDescription = this.competitionDescription.tasks[index] + this.currentTaskDescription = this.description.tasks[index] /* Update RunManager status. */ this.status = RunManagerStatus.ACTIVE @@ -298,6 +295,9 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.currentTaskDescription}") } + /** List of [InteractiveSynchronousCompetition.Task] for this [InteractiveSynchronousRunManager]. */ + override fun tasks(context: RunActionContext): List = this.run.tasks + /** * Returns the currently active [InteractiveSynchronousCompetition.Task]s or null, if no such task is active. * @@ -521,7 +521,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio // oh shit, something went horribly horribly wrong if (errorCounter >= maxErrorCount){ LOGGER.error("Reached maximum consecutive error count, terminating loop") - this.persistCurrentRunInformation() + RunExecutor.dump(this.run) break //terminate loop } } @@ -535,19 +535,6 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio LOGGER.info("SynchronousRunManager ${this.id} reached end of run logic.") } - /** - * Tries to persist the information of a current run in case something goes horribly wrong - */ - private fun persistCurrentRunInformation() { - try{ - val file = File("run_dump_${this.run.id.string}.json") - jacksonObjectMapper().writeValue(file, this.run) - LOGGER.info("Wrote current run state to ${file.absolutePath}") - } catch (e: Exception){ - LOGGER.error("Could not write run to disk: ", e) - } - } - /** * Invokes all [Updatable]s registered with this [InteractiveSynchronousRunManager]. */ diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 4605a1bb1..e422588cd 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -36,11 +36,11 @@ class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager get() = this.run.name /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ - override val competitionDescription: CompetitionDescription + override val description: CompetitionDescription get() = this.run.description /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.competitionDescription.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.description.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) override val scoreboards: List get() = this.scoreboardsUpdatable.scoreboards diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 3d485cdf1..57d2ac182 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -1,5 +1,6 @@ package dev.dres.run +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.run.websocket.ClientMessage @@ -18,6 +19,7 @@ import dev.dres.utilities.extensions.write import io.javalin.websocket.WsContext import io.javalin.websocket.WsHandler import org.slf4j.LoggerFactory +import java.io.File import java.util.* import java.util.concurrent.Executors import java.util.concurrent.Future @@ -237,6 +239,22 @@ object RunExecutor : Consumer { * Stops all runs */ fun stop() { - executor.shutdownNow() + this.executor.shutdownNow() + } + + + /** + * Dumps the given [Competition] to a file. + * + * @param competition [Competition] that should be dumped. + */ + fun dump(competition: Competition) { + try { + val file = File("run_dump_${competition.id.string}.json") + jacksonObjectMapper().writeValue(file, competition) + this.logger.info("Wrote current run state to ${file.absolutePath}") + } catch (e: Exception){ + this.logger.error("Could not write run to disk: ", e) + } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index b1f27cd28..f181eeee9 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -26,7 +26,7 @@ interface RunManager : Runnable { val name: String /** The [CompetitionDescription] that is executed / run by this [RunManager]. */ - val competitionDescription: CompetitionDescription + val description: CompetitionDescription /** List of [Scoreboard]s for this [RunManager]. */ val scoreboards: List diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 69b50d36a..861f1cb01 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -22,7 +22,7 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte if (this.run.timeLeft(context) > 0) { if (this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { /* Determine of all teams have submitted . */ - val allDone = this.run.competitionDescription.teams.all { team -> + val allDone = this.run.description.teams.all { team -> this.run.submissions(this.context).count { it.teamId == team.uid && it.status == SubmissionStatus.CORRECT } >= limit } From b0aa859cd4f80fdc4306481d4e44d74f24960d03 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 6 Mar 2021 17:20:58 +0100 Subject: [PATCH 47/95] Added first SubmissionBatchValidators --- .../model/run/AbstractNonInteractiveTask.kt | 10 +++--- .../dev/dres/run/NonInteractiveRunManager.kt | 7 ++-- .../run/updatables/ScoreboardsUpdatable.kt | 2 +- .../MediaItemsSubmissionBatchValidator.kt | 17 +++++++++ .../MediaItemsSubmissionValidator.kt | 3 +- ...TemporalOverlapSubmissionBatchValidator.kt | 35 +++++++++++++++++++ .../TemporalOverlapSubmissionValidator.kt | 6 ++-- 7 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index 3ab31934e..61aa3a500 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -1,10 +1,12 @@ package dev.dres.data.model.run +import dev.dres.data.model.competition.TaskDescriptionTarget import dev.dres.data.model.competition.TaskType import dev.dres.data.model.run.interfaces.Task -import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.aspects.OriginAspect import dev.dres.data.model.submissions.batch.ResultBatch +import dev.dres.run.validation.MediaItemsSubmissionBatchValidator +import dev.dres.run.validation.TemporalOverlapSubmissionBatchValidator import dev.dres.run.validation.interfaces.SubmissionBatchValidator /** @@ -19,9 +21,9 @@ abstract class AbstractNonInteractiveTask: AbstractRun(), Task { val validator: SubmissionBatchValidator = newValidator() internal fun newValidator(): SubmissionBatchValidator = when(this.description.taskType.targetType.option){ - TaskType.TargetType.SINGLE_MEDIA_ITEM -> TODO() - TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TODO() - TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> TODO() + TaskType.TargetType.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionBatchValidator(setOf((description.target as TaskDescriptionTarget.MediaItemTarget).item)) + TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionBatchValidator(description.target as TaskDescriptionTarget.VideoSegmentTarget) + TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionBatchValidator((description.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) TaskType.TargetType.JUDGEMENT -> TODO() TaskType.TargetType.VOTE -> TODO() } diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index e422588cd..44a6ddb42 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -6,7 +6,9 @@ import dev.dres.api.rest.types.run.websocket.ClientMessageType import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.* +import dev.dres.data.model.run.AbstractNonInteractiveTask +import dev.dres.data.model.run.NonInteractiveCompetition +import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.batch.SubmissionBatch import dev.dres.run.score.scoreboard.Scoreboard @@ -40,7 +42,7 @@ class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager get() = this.run.description /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.description.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.description.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) //TODO requires some changes override val scoreboards: List get() = this.scoreboardsUpdatable.scoreboards @@ -107,6 +109,7 @@ class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager val batches = idNamePair.second.mapNotNull { task.submissions[it] } val validator = task.validator + val scorer = task.scorer batches.forEach { validator.validate(it) diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt index abf8e645c..ba38ad7a8 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt @@ -15,7 +15,7 @@ import java.util.* * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -class ScoreboardsUpdatable(val scoreboards: List, val updateIntervalMs: Long, private val competition: Competition): StatefulUpdatable { +class ScoreboardsUpdatable(val scoreboards: List, private val updateIntervalMs: Long, private val competition: Competition): StatefulUpdatable { companion object { val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED, RunManagerStatus.PREPARING_TASK) diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt new file mode 100644 index 000000000..9c0521fae --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt @@ -0,0 +1,17 @@ +package dev.dres.run.validation + +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.batch.ResultBatch +import dev.dres.run.validation.interfaces.SubmissionBatchValidator + +class MediaItemsSubmissionBatchValidator(private val items : Set) : SubmissionBatchValidator { + + override fun validate(batch: ResultBatch<*>) { + + batch.results.forEach { + it.status = if (it.item in items) SubmissionStatus.CORRECT else SubmissionStatus.WRONG + } + + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index 86a63ebb4..34b2c0666 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -14,8 +14,7 @@ class MediaItemsSubmissionValidator(private val items : Set) : Submis } } - override val deferring: Boolean - get() = false + override val deferring = false } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt new file mode 100644 index 000000000..1103e57ef --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt @@ -0,0 +1,35 @@ +package dev.dres.run.validation + +import dev.dres.data.model.competition.VideoSegment +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.TemporalAspect +import dev.dres.data.model.submissions.batch.ResultBatch +import dev.dres.run.validation.interfaces.SubmissionBatchValidator +import dev.dres.utilities.TimeUtil + +class TemporalOverlapSubmissionBatchValidator(private val targetSegment: VideoSegment) : SubmissionBatchValidator { + + override fun validate(batch: ResultBatch<*>) { + + batch.results.forEach { + if (it is TemporalAspect){ + it.status = when { + it.start > it.end -> SubmissionStatus.WRONG + it.item != targetSegment.item -> SubmissionStatus.WRONG + else -> { + val outer = + TimeUtil.toMilliseconds(this.targetSegment.temporalRange, this.targetSegment.item.fps) + if ((outer.first <= it.start && outer.second >= it.start) || (outer.first <= it.end && outer.second >= it.end)) { + SubmissionStatus.CORRECT + } else { + SubmissionStatus.WRONG + } + } + } + } else { + it.status = SubmissionStatus.WRONG + } + } + + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 50a13a905..492e7eea3 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -15,7 +15,7 @@ import dev.dres.utilities.TimeUtil * @author Luca Rossetto & Ralph Gasser * @version 1.0 */ -class TemporalOverlapSubmissionValidator(private val task: VideoSegment) : SubmissionValidator { +class TemporalOverlapSubmissionValidator(private val targetSegment: VideoSegment) : SubmissionValidator { /** * Validates a [Submission] based on the target segment and the temporal overlap of the @@ -30,9 +30,9 @@ class TemporalOverlapSubmissionValidator(private val task: VideoSegment) : Submi } submission.status = when { submission.start > submission.end -> SubmissionStatus.WRONG - submission.item != task.item -> SubmissionStatus.WRONG + submission.item != targetSegment.item -> SubmissionStatus.WRONG else -> { - val outer = TimeUtil.toMilliseconds(this.task.temporalRange, this.task.item.fps) + val outer = TimeUtil.toMilliseconds(this.targetSegment.temporalRange, this.targetSegment.item.fps) if ((outer.first <= submission.start && outer.second >= submission.start) || (outer.first <= submission.end && outer.second >= submission.end)) { SubmissionStatus.CORRECT } else { From b038c6391077bca04bcc5e6b1a02b88de8cc712b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 6 Mar 2021 19:57:17 +0100 Subject: [PATCH 48/95] Started towards scoring mechanism for asynchronous runs --- .../scorer/InferredAveragePrecisionScorer.kt | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt new file mode 100644 index 000000000..f06c9e4ce --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -0,0 +1,68 @@ +package dev.dres.run.score.scorer + +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.StatusAspect +import dev.dres.data.model.submissions.batch.ResultBatch + +class InferredAveragePrecisionScorer { + + companion object { + + private val epsilon = 0.01 //TODO check what TRECVID uses + + + //see https://www-nlpir.nist.gov/projects/tv2006/infap/inferredAP.pdf + fun infAP(elements: List): Double { + + if (elements.isEmpty()) { + return 0.0 + } + + var infAPSum = 0.0 + var judgements = 0 + var correct = 0 + var wrong = 0 + + elements.forEachIndexed { index, statusAspect -> + + val k = index + 1.0 + when(statusAspect.status) { + SubmissionStatus.CORRECT -> { + ++judgements // |d100| + ++correct // |rel| + + val ap = if (index == 0){ //special case for first document + 1.0 //all are relevant so far, since there is only one so far and it is relevant + } else { + (1.0 / k) + ((k - 1.0) / k) * ((judgements / (k - 1.0)) * ((correct + epsilon) / (correct + wrong + 2.0 * epsilon))) + } + + println(ap) + + infAPSum += ap + + } + SubmissionStatus.WRONG -> { + ++judgements + ++wrong // |nonrel| + } + else -> {} + } + + } + + if (correct == 0){ + return 0.0 + } + + return infAPSum / correct + + } + + fun score(submissions: List): Double = infAP(submissions) + fun score(batch: ResultBatch<*>): Double = infAP(batch.results) + + } + +} \ No newline at end of file From 37bfa448852a090c564b7519b8c08292f71ede36 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 7 Mar 2021 11:28:30 +0100 Subject: [PATCH 49/95] Scoring structures can now also handle non-interactive runs --- .../src/main/kotlin/dev/dres/Playground.kt | 168 ++++++++++-------- .../rest/handler/BatchSubmissionHandler.kt | 4 +- .../handler/CompetitionRunScoreHandler.kt | 16 +- .../submissions/batch/BaseResultBatch.kt | 2 + .../model/submissions/batch/ResultBatch.kt | 2 + .../handlers/TeamCombinationScoreHandler.kt | 26 ++- .../kotlin/dev/dres/run/score/ScoreEntry.kt | 5 + .../kotlin/dev/dres/run/score/TaskContext.kt | 5 + ....kt => IncrementalSubmissionTaskScorer.kt} | 6 +- .../RecalculatingSubmissionTaskScorer.kt | 26 +++ .../interfaces/RecalculatingTaskScorer.kt | 27 --- .../score/interfaces/ResultBatchTaskScorer.kt | 9 + .../dres/run/score/interfaces/TaskScorer.kt | 4 +- .../run/score/interfaces/TeamTaskScorer.kt | 9 + .../scoreboard/MaxNormalizingScoreBoard.kt | 7 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 21 ++- .../scorer/InferredAveragePrecisionScorer.kt | 21 ++- .../dres/run/score/scorer/KisTaskScorer.kt | 22 ++- .../dres/run/updatables/ScoresUpdatable.kt | 13 +- .../run/score/scorer/AvsTaskScorerTest.kt | 164 ++++++++--------- .../run/score/scorer/KisTaskScorerTest.kt | 45 ++--- 21 files changed, 352 insertions(+), 250 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/run/score/ScoreEntry.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt rename backend/src/main/kotlin/dev/dres/run/score/interfaces/{IncrementalTaskScorer.kt => IncrementalSubmissionTaskScorer.kt} (68%) create mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskScorer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/ResultBatchTaskScorer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt diff --git a/backend/src/main/kotlin/dev/dres/Playground.kt b/backend/src/main/kotlin/dev/dres/Playground.kt index 7c76ff809..fa26479ca 100644 --- a/backend/src/main/kotlin/dev/dres/Playground.kt +++ b/backend/src/main/kotlin/dev/dres/Playground.kt @@ -1,98 +1,108 @@ package dev.dres -import dev.dres.data.dbo.DAO -import dev.dres.data.model.UID -import dev.dres.data.model.competition.TaskDescriptionTarget -import dev.dres.data.serializers.AuditLogEntrySerializer -import dev.dres.data.serializers.CompetitionSerializer -import dev.dres.data.serializers.MediaItemSerializer -import dev.dres.run.audit.SubmissionAuditLogEntry -import dev.dres.run.audit.TaskStartAuditLogEntry -import java.io.File -import java.nio.file.Paths +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.aspects.StatusAspect +import dev.dres.run.score.scorer.InferredAveragePrecisionScorer object Playground { + data class StatusConrainer(override var status: SubmissionStatus) : StatusAspect + @JvmStatic fun main(args: Array) { - val audit = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/auditLog.db"), AuditLogEntrySerializer) - - val mediaItems = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/mediaItems.db"), MediaItemSerializer) - val competitionSerializer = CompetitionSerializer(mediaItems) + val elements = listOf( + StatusConrainer(SubmissionStatus.CORRECT), + StatusConrainer(SubmissionStatus.CORRECT), + StatusConrainer(SubmissionStatus.CORRECT), + StatusConrainer(SubmissionStatus.CORRECT), + StatusConrainer(SubmissionStatus.CORRECT) - val competitions = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/competitions.db"), competitionSerializer) + ) + println(InferredAveragePrecisionScorer.infAP(elements)) - val competition = competitions[UID("db2ef71a-df42-4fbd-a2ea-2e1c8b393edd")]!! - val targets = competition.tasks.map { - it.name to (it.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.map { it.name } - }.toMap() - val teams = competition.teams.map { it.name } - -// println(audit.find { it.id.string == "b54fc9dd-5a86-4c92-be7b-7642b35c887b" }!!.timestamp) // -// return - - val events = audit.filter { it.timestamp > 1604053744241 }.sortedBy { it.timestamp } - - val taskStartTimes = mutableMapOf() - - val submissionWriter = File("submissions.csv").printWriter() - val taskStartWriter = File("taskStart.csv").printWriter() - - submissionWriter.println("timestamp,task,time,team,member,session,item,correct") - taskStartWriter.println("timestamp,task") - - for (event in events) { - - when(event) { - - is TaskStartAuditLogEntry -> { - - taskStartTimes[event.taskName] = event.timestamp - taskStartWriter.println("${event.timestamp},${event.taskName}") - - } - - - is SubmissionAuditLogEntry -> { - - val taskName = event.taskName - - if (!targets.containsKey(taskName)){ - println("ignoring $taskName") - continue - } - - val time = event.submission.timestamp - taskStartTimes[taskName]!! - val item = event.submission.item.name - val correct = (targets[taskName] ?: - error("$taskName not found")).contains(item) - - - - //submissionWriter.println("${event.timestamp},$taskName,$time,${teams[event.submission.teamId]},${event.submission.member.string},${event.user},$item,$correct") - - } - - else -> { - println("ignoring $event") - } - - } - - } - - submissionWriter.flush() - submissionWriter.close() - taskStartWriter.flush() - taskStartWriter.close() - +// +// val audit = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/auditLog.db"), AuditLogEntrySerializer) +// +// val mediaItems = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/mediaItems.db"), MediaItemSerializer) +// val competitionSerializer = CompetitionSerializer(mediaItems) +// +// val competitions = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/competitions.db"), competitionSerializer) +// +// +// val competition = competitions[UID("db2ef71a-df42-4fbd-a2ea-2e1c8b393edd")]!! +// +// val targets = competition.tasks.map { +// it.name to (it.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.map { it.name } +// }.toMap() +// +// val teams = competition.teams.map { it.name } +// +//// println(audit.find { it.id.string == "b54fc9dd-5a86-4c92-be7b-7642b35c887b" }!!.timestamp) +//// +//// return +// +// val events = audit.filter { it.timestamp > 1604053744241 }.sortedBy { it.timestamp } +// +// val taskStartTimes = mutableMapOf() +// +// val submissionWriter = File("submissions.csv").printWriter() +// val taskStartWriter = File("taskStart.csv").printWriter() +// +// submissionWriter.println("timestamp,task,time,team,member,session,item,correct") +// taskStartWriter.println("timestamp,task") +// +// for (event in events) { +// +// when(event) { +// +// is TaskStartAuditLogEntry -> { +// +// taskStartTimes[event.taskName] = event.timestamp +// taskStartWriter.println("${event.timestamp},${event.taskName}") +// +// } +// +// +// is SubmissionAuditLogEntry -> { +// +// val taskName = event.taskName +// +// if (!targets.containsKey(taskName)){ +// println("ignoring $taskName") +// continue +// } +// +// val time = event.submission.timestamp - taskStartTimes[taskName]!! +// val item = event.submission.item.name +// val correct = (targets[taskName] ?: +// error("$taskName not found")).contains(item) +// +// +// +// //submissionWriter.println("${event.timestamp},$taskName,$time,${teams[event.submission.teamId]},${event.submission.member.string},${event.user},$item,$correct") +// +// } +// +// else -> { +// println("ignoring $event") +// } +// +// } +// +// } +// +// submissionWriter.flush() +// submissionWriter.close() +// taskStartWriter.flush() +// taskStartWriter.close() +// } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt index 7cba9bb86..81c04e9e7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt @@ -97,9 +97,9 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D if (results.all { it is TemporalBatchElement }) { @Suppress("UNCHECKED_CAST") - (BaseResultBatch(mediaCollectionId, batch.resultName, results as List)) + (BaseResultBatch(mediaCollectionId, batch.resultName, team, results as List)) } else { - BaseResultBatch(mediaCollectionId, batch.resultName, results) + BaseResultBatch(mediaCollectionId, batch.resultName, team, results) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index ef2f2498a..4c0c59d09 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -8,6 +8,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor +import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.score.scoreboard.Score import dev.dres.run.score.scoreboard.ScoreOverview import dev.dres.utilities.extensions.UID @@ -116,7 +117,9 @@ class CurrentTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler @@ -166,9 +169,12 @@ class HistoryTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler - task.scorer.scores().map { "${task.description.name},${task.description.taskGroup.name},${run.description.teams.find { t -> t.uid == it.key }?.name ?: "???"},${it.value}" } + return "task,group,team,resultName,score\n" + run.tasks(rac).filter { it.started != null}.sortedBy { it.started }.flatMap { task -> + task.scorer.scores().map { "${task.description.name},${task.description.taskGroup.name},${run.description.teams.find { t -> t.uid == it.first }?.name ?: "???"},${it.second ?: "n/a"},${it.third}" } }.joinToString(separator = "\n") } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt index 8df5e68af..64dcc66b0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.submissions.batch +import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.interfaces.TaskId /** @@ -10,5 +11,6 @@ import dev.dres.data.model.run.interfaces.TaskId data class BaseResultBatch( override val task: TaskId, override val name: String, + override val teamId: TeamId, override val results: List ) : ResultBatch \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt index 287edc090..87b52c865 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.submissions.batch +import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.interfaces.TaskId /** @@ -9,6 +10,7 @@ import dev.dres.data.model.run.interfaces.TaskId */ interface ResultBatch { val task: TaskId + val teamId: TeamId val name: String val results: List } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index ba5b1ca85..e92b6b21b 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -4,8 +4,10 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* -import dev.dres.run.score.interfaces.IncrementalTaskScorer -import dev.dres.run.score.interfaces.RecalculatingTaskScorer +import dev.dres.run.score.TaskContext +import dev.dres.run.score.interfaces.IncrementalSubmissionTaskScorer +import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer +import dev.dres.run.score.interfaces.TeamTaskScorer import java.io.File import java.io.PrintWriter @@ -38,6 +40,10 @@ class TeamCombinationScoreHandler : StreamEventHandler { val scorer = taskDescription.newScorer() + if (scorer !is TeamTaskScorer) { + return + } + val submissions = submissionTaskMap[event.taskId] ?: return val teams = submissions.map { it.teamId }.toSet().toList().sortedBy { it.string } @@ -60,22 +66,24 @@ class TeamCombinationScoreHandler : StreamEventHandler { } when(scorer) { - is RecalculatingTaskScorer -> { + is RecalculatingSubmissionTaskScorer -> { scorer.computeScores( combinedSubmissions, - combinations.keys, - taskStartMap[event.taskId]!!, - taskDescription.duration, - event.timeStamp + TaskContext( + combinations.keys, + taskStartMap[event.taskId]!!, + taskDescription.duration, + event.timeStamp + ) ) } - is IncrementalTaskScorer -> { + is IncrementalSubmissionTaskScorer -> { combinedSubmissions.forEach { scorer.update(it) } } else -> throw IllegalStateException("unsupported scorer type $scorer") } - val scores = scorer.scores().mapKeys { combinations[it.key]!! } + val scores = scorer.teamScoreMap().mapKeys { combinations[it.key]!! } scores.forEach { writer.println("${event.taskId.string},${it.key.first.string},${it.key.second.string},${it.value}") diff --git a/backend/src/main/kotlin/dev/dres/run/score/ScoreEntry.kt b/backend/src/main/kotlin/dev/dres/run/score/ScoreEntry.kt new file mode 100644 index 000000000..31a4c5c00 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/ScoreEntry.kt @@ -0,0 +1,5 @@ +package dev.dres.run.score + +import dev.dres.data.model.competition.TeamId + +typealias ScoreEntry = Triple \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt new file mode 100644 index 000000000..6fc50b58e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt @@ -0,0 +1,5 @@ +package dev.dres.run.score + +import dev.dres.data.model.competition.TeamId + +data class TaskContext(val teamIds: Collection, val taskStartTime: Long?, val taskDuration: Long?, val taskEndTime: Long? = null) diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt similarity index 68% rename from backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskScorer.kt rename to backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt index b5a7be812..6e6459d03 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt @@ -8,11 +8,11 @@ import dev.dres.data.model.submissions.Submission * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -interface IncrementalTaskScorer: TaskScorer { +interface IncrementalSubmissionTaskScorer: TaskScorer { /** - * Updates this [IncrementalTaskScorer]'s score just using a single submission. + * Updates this [IncrementalSubmissionTaskScorer]'s score just using a single submission. * - * @param submission The [Submission] to update this [IncrementalTaskScorer] with. + * @param submission The [Submission] to update this [IncrementalSubmissionTaskScorer] with. */ fun update(submission: Submission) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt new file mode 100644 index 000000000..7f804cda6 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt @@ -0,0 +1,26 @@ +package dev.dres.run.score.interfaces + +import dev.dres.data.model.UID +import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.submissions.Submission +import dev.dres.run.score.TaskContext + +/** + * A [TaskScorer] implementation that re-computes the current scores of all teams for a given [Task] based on the + * entire [Submission] history. As opposed to the [IncrementalSubmissionTaskScorer], incremental updates are not possible. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.2 + */ +interface RecalculatingSubmissionTaskScorer: TaskScorer { + /** + * Re-computes this [RecalculatingSubmissionTaskScorer]'s score based on the given [Submission] history. + * + * @param submissions The [Submission]s used to update this [RecalculatingSubmissionTaskScorer] with. + * @param taskStartTime Time the [Task] started. + * @param taskDuration Duration of the [Task]. + * @param taskEndTime Time the [Task] ended. + * + */ + fun computeScores(submissions: Collection, context: TaskContext): Map +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskScorer.kt deleted file mode 100644 index 6f19a6795..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingTaskScorer.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dev.dres.run.score.interfaces - -import dev.dres.data.model.UID -import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.interfaces.Task -import dev.dres.data.model.submissions.Submission - -/** - * A [TaskScorer] implementation that re-computes the current scores of all teams for a given [Task] based on the - * entire [Submission] history. As opposed to the [IncrementalTaskScorer], incremental updates are not possible. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.2 - */ -interface RecalculatingTaskScorer: TaskScorer { - /** - * Re-computes this [RecalculatingTaskScorer]'s score based on the given [Submission] history. - * - * @param submissions The [Submission]s used to update this [RecalculatingTaskScorer] with. - * @param taskStartTime Time the [Task] started. - * @param taskDuration Duration of the [Task]. - * @param taskEndTime Time the [Task] ended. - * - * TODO: Should we maybe introduce a "TaskContext" here instead of handing over these individual parameters? - */ - fun computeScores(submissions: Collection, teamIds: Collection, taskStartTime: Long, taskDuration: Long, taskEndTime: Long = 0): Map -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/ResultBatchTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/ResultBatchTaskScorer.kt new file mode 100644 index 000000000..c57672f7a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/ResultBatchTaskScorer.kt @@ -0,0 +1,9 @@ +package dev.dres.run.score.interfaces + +import dev.dres.data.model.submissions.batch.ResultBatch + +interface ResultBatchTaskScorer : TaskScorer { + + fun computeScores(batch: ResultBatch<*>) : Double + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt index 96d7f474a..bbf9a7192 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt @@ -3,6 +3,7 @@ package dev.dres.run.score.interfaces import dev.dres.data.model.UID import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.interfaces.Task +import dev.dres.run.score.ScoreEntry /** * A scorer for a [Task]. A score is a [Double] value that captures a teams performance. @@ -17,5 +18,6 @@ interface TaskScorer { * * @return Map of team [UID] to team score */ - fun scores(): Map + fun scores(): List + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt new file mode 100644 index 000000000..4d781bd6c --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt @@ -0,0 +1,9 @@ +package dev.dres.run.score.interfaces + +import dev.dres.data.model.competition.TeamId + +interface TeamTaskScorer : TaskScorer { + + fun teamScoreMap() : Map + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index f1bf2af24..77ceb1adb 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -37,7 +37,12 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, pri override fun update(scorers: Map) { this.scorePerTaskMap.clear() - this.scorePerTaskMap.putAll(scorers.map { it.key to it.value.scores() }.toMap()) + this.scorePerTaskMap.putAll(scorers.map { + it.key to it.value.scores().groupBy { it.first }.mapValues { + it.value.maxByOrNull { it.third }?.third ?: 0.0 + } + }.toMap() + ) } override fun update(runs: List) { diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index c3ec13b95..f9dd53c55 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -5,18 +5,21 @@ import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.competition.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.run.score.interfaces.RecalculatingTaskScorer +import dev.dres.run.score.ScoreEntry +import dev.dres.run.score.TaskContext +import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer +import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.utilities.TimeUtil import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write -class AvsTaskScorer: RecalculatingTaskScorer { +class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() - override fun computeScores(submissions: Collection, teamIds: Collection, taskStartTime: Long, taskDuration: Long, taskEndTime: Long): Map { + override fun computeScores(submissions: Collection, context: TaskContext): Map { val correctSubmissions = submissions.filter { it.status == SubmissionStatus.CORRECT } val wrongSubmissions = submissions.filter { it.status == SubmissionStatus.WRONG } @@ -27,7 +30,7 @@ class AvsTaskScorer: RecalculatingTaskScorer { val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() lastScores = this.lastScoresLock.write { - teamIds.map { teamid -> + context.teamIds.map { teamid -> val correctSubs = correctSubmissionsPerTeam[teamid] ?: return@map teamid to 0.0 @@ -42,7 +45,7 @@ class AvsTaskScorer: RecalculatingTaskScorer { } - return scores() + return teamScoreMap() } private fun countQuantized(submissions: Collection): Int { @@ -59,5 +62,11 @@ class AvsTaskScorer: RecalculatingTaskScorer { } - override fun scores(): Map = this.lastScoresLock.read { this.lastScores } + override fun teamScoreMap(): Map = this.lastScoresLock.read { this.lastScores } + + override fun scores(): List = this.lastScoresLock.read { + this.lastScores.map { ScoreEntry(it.key, null, it.value) } + } + + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index f06c9e4ce..6734a0872 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -1,11 +1,17 @@ package dev.dres.run.score.scorer +import dev.dres.data.model.competition.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.StatusAspect import dev.dres.data.model.submissions.batch.ResultBatch +import dev.dres.run.score.ScoreEntry +import dev.dres.run.score.interfaces.ResultBatchTaskScorer +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read +import kotlin.concurrent.write -class InferredAveragePrecisionScorer { +class InferredAveragePrecisionScorer : ResultBatchTaskScorer { companion object { @@ -65,4 +71,17 @@ class InferredAveragePrecisionScorer { } + private var lastScores: MutableMap, Double> = mutableMapOf() + private val lastScoresLock = ReentrantReadWriteLock() + + override fun computeScores(batch: ResultBatch<*>): Double = this.lastScoresLock.write { + val score = score(batch) + this.lastScores[batch.teamId to batch.name] = score + return@write score + } + + override fun scores(): List = this.lastScoresLock.read { + this.lastScores.map { ScoreEntry(it.key.first, it.key.second, it.value) } + } + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index bcafbb875..09e49591b 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -4,7 +4,10 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.run.score.interfaces.RecalculatingTaskScorer +import dev.dres.run.score.ScoreEntry +import dev.dres.run.score.TaskContext +import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer +import dev.dres.run.score.interfaces.TeamTaskScorer import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write @@ -14,7 +17,7 @@ class KisTaskScorer( private val maxPointsPerTask: Double = defaultmaxPointsPerTask, private val maxPointsAtTaskEnd: Double = defaultmaxPointsAtTaskEnd, private val penaltyPerWrongSubmission: Double = defaultpenaltyPerWrongSubmission -) : RecalculatingTaskScorer { +) : RecalculatingSubmissionTaskScorer, TeamTaskScorer { constructor(parameters: Map) : this( parameters.getOrDefault("maxPointsPerTask", "$defaultmaxPointsPerTask").toDoubleOrNull() ?: defaultmaxPointsPerTask, @@ -31,11 +34,14 @@ class KisTaskScorer( private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() - override fun computeScores(submissions: Collection, teamIds: Collection, taskStartTime: Long, taskDuration: Long, taskEndTime: Long): Map = this.lastScoresLock.write { + override fun computeScores(submissions: Collection, context: TaskContext): Map = this.lastScoresLock.write { - val tDur = max(taskDuration * 1000L, taskEndTime - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition + val taskStartTime = context.taskStartTime ?: throw IllegalArgumentException("no task start time specified") + val taskDuration = context.taskDuration ?: throw IllegalArgumentException("no task duration specified") - this.lastScores = teamIds.map { teamId -> + val tDur = max(taskDuration * 1000L, (context.taskEndTime ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition + + this.lastScores = context.teamIds.map { teamId -> val sbs = submissions.filter { it.teamId == teamId && (it.status == SubmissionStatus.CORRECT || it.status == SubmissionStatus.WRONG) }.sortedBy { it.timestamp } val firstCorrect = sbs.indexOfFirst { it.status == SubmissionStatus.CORRECT } val score = if (firstCorrect > -1) { @@ -55,5 +61,9 @@ class KisTaskScorer( return this.lastScores } - override fun scores(): Map = this.lastScoresLock.read { this.lastScores } + override fun teamScoreMap(): Map = this.lastScoresLock.read { this.lastScores } + + override fun scores(): List = this.lastScoresLock.read { + this.lastScores.map { ScoreEntry(it.key, null, it.value) } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index aa48b3147..9d9636bc0 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -8,8 +8,9 @@ import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.RunManagerStatus -import dev.dres.run.score.interfaces.IncrementalTaskScorer -import dev.dres.run.score.interfaces.RecalculatingTaskScorer +import dev.dres.run.score.TaskContext +import dev.dres.run.score.interfaces.IncrementalSubmissionTaskScorer +import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import java.util.* /** @@ -36,13 +37,13 @@ class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdat override fun update(status: RunManagerStatus) { if (!this.list.isEmpty()) { - val scorersToUpdate = mutableSetOf>() + val scorersToUpdate = mutableSetOf>() val removed = this.list.removeIf { val scorer = it.first.scorer if (it.second.status != SubmissionStatus.INDETERMINATE) { when(scorer) { - is RecalculatingTaskScorer -> scorersToUpdate.add(Pair(it.first, scorer)) - is IncrementalTaskScorer -> scorer.update(it.second) + is RecalculatingSubmissionTaskScorer -> scorersToUpdate.add(Pair(it.first, scorer)) + is IncrementalSubmissionTaskScorer -> scorer.update(it.second) else -> { } } true @@ -55,7 +56,7 @@ class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdat scorersToUpdate.forEach { val task = it.first if (it.first.started != null) { - it.second.computeScores(task.submissions, task.competition.description.teams.map { t -> t.uid }, task.started!!, task.description.duration, task.ended ?: 0) + it.second.computeScores(task.submissions, TaskContext(task.competition.description.teams.map { t -> t.uid }, task.started, task.description.duration, task.ended)) } } diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index ff7463abf..6e731d809 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -2,9 +2,9 @@ package dres.run.score.scorer import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.run.ItemSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.run.TemporalSubmission +import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.AvsTaskScorer import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -48,7 +48,7 @@ class AvsTaskScorerTest { @Test fun noSubmissions() { - val scores = this.scorer.computeScores(emptyList(), teams, 100_000, defaultTaskDuration) + val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -58,11 +58,11 @@ class AvsTaskScorerTest { fun allWrongSameImage() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - ItemSubmission(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -72,11 +72,11 @@ class AvsTaskScorerTest { fun allWrongDifferentImages() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - ItemSubmission(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[1], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[2], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.WRONG } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -86,11 +86,11 @@ class AvsTaskScorerTest { fun allSameImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - ItemSubmission(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) assertEquals(100.0, scores[teams[1]]) assertEquals(100.0, scores[teams[2]]) @@ -100,11 +100,11 @@ class AvsTaskScorerTest { fun allDifferentImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - ItemSubmission(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[1], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[2], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) assertEquals(100.0 / 3.0, scores[teams[1]]!!, 0.001) assertEquals(100.0 / 3.0, scores[teams[2]]!!, 0.001) @@ -121,24 +121,24 @@ class AvsTaskScorerTest { */ //3 out of 4 correct - ItemSubmission(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[0], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[0], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[0], UID(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], UID(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, //1 out of 3 correct - ItemSubmission(teams[1], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[1], UID(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[1], UID(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], UID(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, //1 out of 2 correct - ItemSubmission(teams[2], UID(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[2], UID(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[2], UID(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) /* c = q(c) = 3, i = 1, q(p) = 4 @@ -173,11 +173,11 @@ class AvsTaskScorerTest { fun allSameVideoSameSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - TemporalSubmission(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) assertEquals(100.0, scores[teams[1]]) assertEquals(100.0, scores[teams[2]]) @@ -188,11 +188,11 @@ class AvsTaskScorerTest { fun allSameVideoDifferentSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - TemporalSubmission(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) @@ -203,11 +203,11 @@ class AvsTaskScorerTest { fun allDifferentVideoDifferentSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - TemporalSubmission(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) @@ -217,19 +217,19 @@ class AvsTaskScorerTest { fun allSameVideoOverlappingSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - TemporalSubmission(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) assertEquals(100.0, scores[teams[1]]) assertEquals(100.0, scores[teams[2]]) @@ -239,25 +239,25 @@ class AvsTaskScorerTest { fun allSameVideoOverlappingSegmentSomeCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - TemporalSubmission(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, - TemporalSubmission(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, - TemporalSubmission(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG } + Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(85.71428571428571, scores[teams[0]]!!, 0.001) assertEquals(85.71428571428571, scores[teams[1]]!!, 0.001) @@ -271,45 +271,45 @@ class AvsTaskScorerTest { val submissions = listOf( //team 1 //gets merged to one - TemporalSubmission(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, //plus 2 independent correct ones - TemporalSubmission(teams[0], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[0], UID(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], UID(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = SubmissionStatus.CORRECT }, //and an incorrect one directly next to it - TemporalSubmission(teams[0], UID(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[0], UID(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = SubmissionStatus.WRONG }, //c = 5, q(c) = 3, i = 1 //team 2 //the center part is missing, so it's counted as two in the quantization - TemporalSubmission(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, //another correct one, same as team 1 - TemporalSubmission(teams[1], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, //and two wrong ones - TemporalSubmission(teams[1], UID(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, - TemporalSubmission(teams[1], UID(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[1], UID(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[1], UID(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, //c = 3, q(c) = 3, i = 2 //team 3 //same as team 2, but with all 3 segments of the 1st video, same as team 1 - TemporalSubmission(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, - TemporalSubmission(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, //another correct one, same as team 1 - TemporalSubmission(teams[2], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, //and two wrong ones - TemporalSubmission(teams[2], UID(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, - TemporalSubmission(teams[2], UID(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[2], UID(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[2], UID(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, //c = 4, q(c) = 2, i = 2 ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) /* c = 5, q(c) = 3, i = 1, q(p) = 3 diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index a13176fec..11c621e9f 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -2,8 +2,9 @@ package dres.run.score.scorer import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.run.ItemSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.KisTaskScorer import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -26,7 +27,7 @@ class KisTaskScorerTest { @Test fun noSubmissions() { - val scores = this.scorer.computeScores(emptyList(), teams, 100_000, defaultTaskDuration) + val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -36,11 +37,11 @@ class KisTaskScorerTest { fun allWrong() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - ItemSubmission(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -50,9 +51,9 @@ class KisTaskScorerTest { fun immediatelyRight() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - ItemSubmission(teams[0], UID(), taskStartTime, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], UID(), taskStartTime, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsPerTask, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -62,9 +63,9 @@ class KisTaskScorerTest { fun rightAtTheEnd() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - ItemSubmission(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) } @@ -72,9 +73,9 @@ class KisTaskScorerTest { fun rightInTheMiddle() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - ItemSubmission(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) } @@ -84,21 +85,21 @@ class KisTaskScorerTest { val submissions = listOf( //incorrect submissions - ItemSubmission(teams[0], UID(), taskStartTime + 1, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[0], UID(), taskStartTime + 1, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[1], UID(), taskStartTime + 2, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[1], UID(), taskStartTime + 3, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], UID(), taskStartTime + 2, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], UID(), taskStartTime + 3, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[2], UID(), taskStartTime + 4, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[2], UID(), taskStartTime + 5, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - ItemSubmission(teams[2], UID(), taskStartTime + 6, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], UID(), taskStartTime + 4, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], UID(), taskStartTime + 5, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], UID(), taskStartTime + 6, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, //correct submissions at 1/2 the task time - ItemSubmission(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[1], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - ItemSubmission(teams[2], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[1], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[2], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, ) - val scores = this.scorer.computeScores(submissions, teams, taskStartTime, defaultTaskDuration) + val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission, scores[teams[0]]) assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 2, scores[teams[1]]) From 9bad173b3a9ac43dabdb695610497cb9a885bc4b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 7 Mar 2021 13:49:56 +0100 Subject: [PATCH 50/95] Added basic run logic for non-interactive runs --- .../run/InteractiveAsynchronousCompetition.kt | 10 ++-- .../run/InteractiveSynchronousCompetition.kt | 4 +- .../model/run/NonInteractiveCompetition.kt | 4 +- .../dev/dres/run/NonInteractiveRunManager.kt | 56 +++++++++++++++---- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index 9489dbbdd..c6b3fa52c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -1,17 +1,15 @@ package dev.dres.data.model.run import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.competition.TaskDescriptionId -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.* +import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task import dev.dres.data.model.run.interfaces.Competition import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -88,7 +86,7 @@ class InteractiveAsynchronousCompetition(override var id: CompetitionId, overrid override val filter: SubmissionFilter = this.description.newFilter() @Transient - override val scorer: TaskScorer = this.description.newScorer() + override val scorer: TeamTaskScorer = this.description.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("specified scorer is not of type TeamTaskScorer") @Transient override val validator: SubmissionValidator = this.newValidator() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt index 4ebf9da46..e25082512 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt @@ -12,7 +12,7 @@ import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import java.util.* @@ -77,7 +77,7 @@ class InteractiveSynchronousCompetition(override var id: CompetitionId, override override val filter: SubmissionFilter = description.newFilter() @Transient - override val scorer: TaskScorer = description.newScorer() + override val scorer: TeamTaskScorer = this.description.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("specified scorer is not of type TeamTaskScorer") @Transient override val validator: SubmissionValidator = newValidator() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt index 761bdeebf..73980d8cf 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt @@ -10,7 +10,7 @@ import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.aspects.OriginAspect import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.interfaces.ResultBatchTaskScorer class NonInteractiveCompetition(override var id: CompetitionId, override val name: String, override val description: CompetitionDescription): AbstractRun(), Competition { @@ -42,6 +42,6 @@ class NonInteractiveCompetition(override var id: CompetitionId, override val nam .find { it.id == this.taskDescriptionId } ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") @Transient - override val scorer: TaskScorer = description.newScorer() + override val scorer: ResultBatchTaskScorer = description.newScorer() as? ResultBatchTaskScorer ?: throw IllegalArgumentException("specified scorer is not of type ResultBatchTaskScorer") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 44a6ddb42..f186aef65 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -12,6 +12,7 @@ import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.batch.SubmissionBatch import dev.dres.run.score.scoreboard.Scoreboard +import dev.dres.run.updatables.DAOUpdatable import dev.dres.run.updatables.ScoreboardsUpdatable import dev.dres.run.validation.interfaces.JudgementValidator import org.slf4j.LoggerFactory @@ -29,6 +30,9 @@ class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager /** A lock for state changes to this [InteractiveSynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() + /** The internal [DAOUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ + private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) + /** Run ID of this [InteractiveSynchronousRunManager]. */ override val id: UID get() = this.run.id @@ -62,11 +66,38 @@ class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager get() = this.run.tasks.map { it.validator }.filterIsInstance(JudgementValidator::class.java) override fun start(context: RunActionContext) { - TODO("Not yet implemented") + check(this.status == RunManagerStatus.CREATED) { "NonInteractiveRunManager is in status ${this.status} and cannot be started." } + if (!context.isAdmin) + throw IllegalAccessError("functionality of NonInteractiveRunManager only available to administrators") + + /* Start the run. */ + this.run.start() + + /* Update status. */ + this.status = RunManagerStatus.ACTIVE + + /* Mark DAO for update. */ + this.daoUpdatable.dirty = true + + + LOGGER.info("NonInteractiveRunManager ${this.id} started") } override fun end(context: RunActionContext) { - TODO("Not yet implemented") + check(this.status != RunManagerStatus.TERMINATED) { "NonInteractiveRunManager is in status ${this.status} and cannot be terminated." } + if (!context.isAdmin) + throw IllegalAccessError("functionality of NonInteractiveRunManager only available to administrators") + + /* End the run. */ + this.run.end() + + /* Update status. */ + this.status = RunManagerStatus.TERMINATED + + /* Mark DAO for update. */ + this.daoUpdatable.dirty = true + + LOGGER.info("SynchronousRunManager ${this.id} terminated") } override fun taskCount(context: RunActionContext): Int = this.run.tasks.size @@ -91,6 +122,8 @@ class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager try { this.stateLock.read { + + while (updatedTasks.isNotEmpty()) { val idNamePair = updatedTasks.poll(1, TimeUnit.SECONDS) @@ -113,31 +146,34 @@ class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager batches.forEach { validator.validate(it) + scorer.computeScores(it) } + scoreboardsUpdatable.update(this.status) - //TODO scoring + this.daoUpdatable.dirty = true + } - scoreboardsUpdatable.update(this.status) + this.daoUpdatable.update(this.status) - } } } catch (ie: InterruptedException) { - LOGGER.info("Interrupted SynchronousRunManager, exiting") + LOGGER.info("Interrupted NonInteractiveRunManager, exiting") return } - - - - Thread.sleep(100) } + this.stateLock.read { + this.daoUpdatable.update(this.status) + } + + LOGGER.info("NonInteractiveRunManager ${this.id} reached end of run logic.") } From 8b1d1fd4cf4d737227d575c9b3f0ea546a4a3db6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 7 Mar 2021 17:15:55 +0100 Subject: [PATCH 51/95] Images hints are now centered. Fixes #230 --- .../query-object-preview/image-object-preview.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/viewer/query-object-preview/image-object-preview.component.ts b/frontend/src/app/viewer/query-object-preview/image-object-preview.component.ts index 2df00fd2a..3c252819c 100644 --- a/frontend/src/app/viewer/query-object-preview/image-object-preview.component.ts +++ b/frontend/src/app/viewer/query-object-preview/image-object-preview.component.ts @@ -8,7 +8,7 @@ import {ContentElement} from '../../../../openapi'; @Component({ selector: 'app-image-object-preview', template: ` -
+
image
` From f3e7e0028ee878ed0d81e20c0630eefcf5cd50e2 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 7 Mar 2021 22:14:02 +0100 Subject: [PATCH 52/95] Refactored Options and added a SimpleOption that prolongs a task on submission (in preparation for #124). --- .../api/rest/handler/CompetitionRunHandler.kt | 7 +- .../api/rest/handler/SubmissionHandler.kt | 7 +- .../tasks/RestTaskDescriptionComponent.kt | 24 +++---- .../tasks/RestTaskDescriptionTarget.kt | 26 ++++---- .../data/model/competition/TaskDescription.kt | 4 +- .../dres/data/model/competition/TaskType.kt | 65 ++++--------------- .../competition/options/ConfiguredOption.kt | 49 ++++++++++++++ .../data/model/competition/options/Option.kt | 10 +++ .../options/QueryComponentOption.kt | 14 ++++ .../competition/options/ScoringOption.kt | 25 +++++++ .../model/competition/options/SimpleOption.kt | 13 ++++ .../options/SimpleOptionParameters.kt | 31 +++++++++ .../options/SubmissionFilterOption.kt | 17 +++++ .../model/competition/options/TargetOption.kt | 14 ++++ .../data/model/run/AbstractInteractiveTask.kt | 11 ++-- .../model/run/AbstractNonInteractiveTask.kt | 11 ++-- .../data/serializers/TaskTypeSerializer.kt | 12 ++-- .../dres/run/updatables/EndTaskUpdatable.kt | 6 +- backend/src/main/resources/html/index.html | 1 + doc/oas.json | 2 +- frontend/package.json | 2 +- ...mpetition-builder-task-dialog.component.ts | 13 ++-- .../competition-form.builder.ts | 6 +- ...tion-builder-task-type-dialog.component.ts | 37 +++++------ 24 files changed, 269 insertions(+), 138 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/Option.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/QueryComponentOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/ScoringOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOptionParameters.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/TargetOption.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index 056095703..12df7b2a0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -16,6 +16,7 @@ import dev.dres.data.model.Config import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.competition.options.SimpleOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor @@ -315,7 +316,7 @@ class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandle /* Obtain current task run and check status. */ return if (run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTaskDescription(rac).taskType.options.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + if (run.currentTaskDescription(rac).taskType.options.any{ it.option == SimpleOption.HIDDEN_RESULTS }) { run.submissions(rac).map { SubmissionInfo.blind(it) } } else { run.submissions(rac).map { SubmissionInfo.withId(it) } @@ -355,7 +356,7 @@ class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRest val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() return if (run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTaskDescription(rac).taskType.options.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS}) { run.submissions(rac).filter { it.timestamp >= timestamp }.map { SubmissionInfo.blind(it) } } else { run.submissions(rac).filter { it.timestamp >= timestamp }.map { SubmissionInfo.withId(it) } @@ -397,7 +398,7 @@ class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRes val taskId = ctx.pathParamMap()["taskId"]?.UID() ?: throw ErrorStatusException(404, "Missing task id", ctx) return if (run.currentTask(rac)?.description?.id == taskId && run.status == RunManagerStatus.RUNNING_TASK) { - if (run.currentTaskDescription(rac).taskType.options.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { + if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { run.submissions(rac).map { SubmissionInfo.blind(it) } } else { run.submissions(rac).map { SubmissionInfo.withId(it) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index 7eb118b2c..0d0189e15 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -14,7 +14,7 @@ import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.basics.media.MediaItemSegmentList import dev.dres.data.model.basics.media.PlayableMediaItem -import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.competition.options.SimpleOption import dev.dres.data.model.run.* import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus @@ -88,8 +88,7 @@ class SubmissionHandler (val collections: DAO, private val item val item = this.itemIndex[collectionId to itemParam].firstOrNull() ?: throw ErrorStatusException(404, "Media item '$itemParam (collection = $collectionId)' could not be found.", ctx) - val mapToSegment = runManager.currentTaskDescription(rac).taskType.options.any { it.option == TaskType.Options.MAP_TO_SEGMENT } == true - + val mapToSegment = runManager.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.MAP_TO_SEGMENT } return when { map.containsKey(PARAMETER_NAME_SHOT) && item is MediaItem.VideoItem -> { val segmentList = segmentIndex[item.id].firstOrNull() ?: throw ErrorStatusException(400, "Item '${item.name}' not found.", ctx) @@ -151,7 +150,7 @@ class SubmissionHandler (val collections: DAO, private val item AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, submission, LogEventSource.REST, ctx.sessionId(), ctx.req.remoteAddr) EventStreamProcessor.event(SubmissionEvent(ctx.sessionId(), run.id, run.currentTask(rac)?.uid, submission)) - if (run.currentTaskDescription(rac).taskType.options.any{ it.option == TaskType.Options.HIDDEN_RESULTS} == true) { //pre-generate preview + if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { //pre-generate preview generatePreview(submission) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt index 0d2246d50..f0d718a87 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt @@ -4,8 +4,10 @@ import dev.dres.data.dbo.DAO import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.basics.time.TemporalRange import dev.dres.data.model.competition.* +import dev.dres.data.model.competition.options.QueryComponentOption import dev.dres.utilities.extensions.UID import java.nio.file.Paths +import javax.management.Query /** * The RESTful API equivalent for [TaskDescriptionHint]. @@ -17,7 +19,7 @@ data class RestTaskDescriptionComponent( /** * The type of this component */ - val type: TaskType.QueryComponentType, + val type: QueryComponentOption, /** * Start time in seconds of when this component is active (Task time) @@ -87,11 +89,11 @@ data class RestTaskDescriptionComponent( * @param hint The [TaskDescriptionHint] to convert. */ fun fromComponent(hint: TaskDescriptionHint) = when(hint) { - is TaskDescriptionHint.TextTaskDescriptionHint -> RestTaskDescriptionComponent(type = TaskType.QueryComponentType.TEXT, start = hint.start, end = hint.end, description = hint.text) - is TaskDescriptionHint.ImageItemTaskDescriptionHint -> RestTaskDescriptionComponent(type = TaskType.QueryComponentType.IMAGE_ITEM, start = hint.start, end = hint.end, mediaItem = hint.item.id.string) - is TaskDescriptionHint.VideoItemSegmentTaskDescriptionHint -> RestTaskDescriptionComponent(type = TaskType.QueryComponentType.VIDEO_ITEM_SEGMENT, start = hint.start, end = hint.end, mediaItem = hint.item.id.string, range = hint.temporalRange) - is TaskDescriptionHint.ExternalImageTaskDescriptionHint -> RestTaskDescriptionComponent(type = TaskType.QueryComponentType.EXTERNAL_IMAGE, path = hint.imageLocation.toString(), start = hint.start, end = hint.end) - is TaskDescriptionHint.ExternalVideoTaskDescriptionHint -> RestTaskDescriptionComponent(type = TaskType.QueryComponentType.EXTERNAL_VIDEO, path = hint.videoLocation.toString(), start = hint.start, end = hint.end) + is TaskDescriptionHint.TextTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.TEXT, start = hint.start, end = hint.end, description = hint.text) + is TaskDescriptionHint.ImageItemTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.IMAGE_ITEM, start = hint.start, end = hint.end, mediaItem = hint.item.id.string) + is TaskDescriptionHint.VideoItemSegmentTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.VIDEO_ITEM_SEGMENT, start = hint.start, end = hint.end, mediaItem = hint.item.id.string, range = hint.temporalRange) + is TaskDescriptionHint.ExternalImageTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.EXTERNAL_IMAGE, path = hint.imageLocation.toString(), start = hint.start, end = hint.end) + is TaskDescriptionHint.ExternalVideoTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.EXTERNAL_VIDEO, path = hint.videoLocation.toString(), start = hint.start, end = hint.end) } } @@ -101,10 +103,10 @@ data class RestTaskDescriptionComponent( * @param mediaItems [DAO] used to perform media item lookups. */ fun toComponent(mediaItems: DAO) = when(this.type){ - TaskType.QueryComponentType.IMAGE_ITEM -> TaskDescriptionHint.ImageItemTaskDescriptionHint(mediaItems[this.mediaItem!!.UID()] as MediaItem.ImageItem, this.start, this.end) - TaskType.QueryComponentType.VIDEO_ITEM_SEGMENT -> TaskDescriptionHint.VideoItemSegmentTaskDescriptionHint(mediaItems[this.mediaItem!!.UID()] as MediaItem.VideoItem, this.range!!, this.start, this.end) - TaskType.QueryComponentType.TEXT -> TaskDescriptionHint.TextTaskDescriptionHint(this.description ?: "", this.start, this.end) - TaskType.QueryComponentType.EXTERNAL_IMAGE -> TaskDescriptionHint.ExternalImageTaskDescriptionHint(Paths.get(this.path ?: throw IllegalArgumentException("Field 'payload' is not specified but required for external image item.")), this.start, this.end) - TaskType.QueryComponentType.EXTERNAL_VIDEO -> TaskDescriptionHint.ExternalVideoTaskDescriptionHint(Paths.get(this.path ?: throw IllegalArgumentException("Field 'payload' is not specified but required for external video item.")), this.start, this.end) + QueryComponentOption.IMAGE_ITEM -> TaskDescriptionHint.ImageItemTaskDescriptionHint(mediaItems[this.mediaItem!!.UID()] as MediaItem.ImageItem, this.start, this.end) + QueryComponentOption.VIDEO_ITEM_SEGMENT -> TaskDescriptionHint.VideoItemSegmentTaskDescriptionHint(mediaItems[this.mediaItem!!.UID()] as MediaItem.VideoItem, this.range!!, this.start, this.end) + QueryComponentOption.TEXT -> TaskDescriptionHint.TextTaskDescriptionHint(this.description ?: "", this.start, this.end) + QueryComponentOption.EXTERNAL_IMAGE -> TaskDescriptionHint.ExternalImageTaskDescriptionHint(Paths.get(this.path ?: throw IllegalArgumentException("Field 'payload' is not specified but required for external image item.")), this.start, this.end) + QueryComponentOption.EXTERNAL_VIDEO -> TaskDescriptionHint.ExternalVideoTaskDescriptionHint(Paths.get(this.path ?: throw IllegalArgumentException("Field 'payload' is not specified but required for external video item.")), this.start, this.end) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTarget.kt index bcb21d1eb..90223f71b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTarget.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTarget.kt @@ -3,16 +3,16 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.data.dbo.DAO import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.competition.TaskDescriptionTarget -import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.competition.options.TargetOption import dev.dres.utilities.extensions.UID /** * The RESTful API equivalent for [TaskDescriptionTarget]. * * @author Luca Rossetto & Ralph Gasser - * @version 1.0 + * @version 1.0.0 */ -data class RestTaskDescriptionTarget(val type: TaskType.TargetType, val mediaItems: List = emptyList()) { +data class RestTaskDescriptionTarget(val type: TargetOption, val mediaItems: List = emptyList()) { companion object { @@ -22,11 +22,11 @@ data class RestTaskDescriptionTarget(val type: TaskType.TargetType, val mediaIte * @param target The [TaskDescriptionTarget] to convert. */ fun fromTarget(target: TaskDescriptionTarget) = when(target) { - is TaskDescriptionTarget.JudgementTaskDescriptionTarget -> RestTaskDescriptionTarget(TaskType.TargetType.JUDGEMENT, target.targets.map { RestTaskDescriptionTargetItem(it.first.id.string, it.second) }) - is TaskDescriptionTarget.VideoSegmentTarget -> RestTaskDescriptionTarget(TaskType.TargetType.SINGLE_MEDIA_SEGMENT, listOf(RestTaskDescriptionTargetItem(target.item.id.string, target.temporalRange))) - is TaskDescriptionTarget.MediaItemTarget -> RestTaskDescriptionTarget(TaskType.TargetType.SINGLE_MEDIA_ITEM, listOf(RestTaskDescriptionTargetItem(target.item.id.string))) - is TaskDescriptionTarget.MultipleMediaItemTarget -> RestTaskDescriptionTarget(TaskType.TargetType.MULTIPLE_MEDIA_ITEMS, target.items.map { RestTaskDescriptionTargetItem(it.id.string) }) - is TaskDescriptionTarget.VoteTaskDescriptionTarget -> RestTaskDescriptionTarget(TaskType.TargetType.VOTE, target.targets.map { RestTaskDescriptionTargetItem(it.first.id.string, it.second) }) + is TaskDescriptionTarget.JudgementTaskDescriptionTarget -> RestTaskDescriptionTarget(TargetOption.JUDGEMENT, target.targets.map { RestTaskDescriptionTargetItem(it.first.id.string, it.second) }) + is TaskDescriptionTarget.VideoSegmentTarget -> RestTaskDescriptionTarget(TargetOption.SINGLE_MEDIA_SEGMENT, listOf(RestTaskDescriptionTargetItem(target.item.id.string, target.temporalRange))) + is TaskDescriptionTarget.MediaItemTarget -> RestTaskDescriptionTarget(TargetOption.SINGLE_MEDIA_ITEM, listOf(RestTaskDescriptionTargetItem(target.item.id.string))) + is TaskDescriptionTarget.MultipleMediaItemTarget -> RestTaskDescriptionTarget(TargetOption.MULTIPLE_MEDIA_ITEMS, target.items.map { RestTaskDescriptionTargetItem(it.id.string) }) + is TaskDescriptionTarget.VoteTaskDescriptionTarget -> RestTaskDescriptionTarget(TargetOption.VOTE, target.targets.map { RestTaskDescriptionTargetItem(it.first.id.string, it.second) }) } } @@ -36,10 +36,10 @@ data class RestTaskDescriptionTarget(val type: TaskType.TargetType, val mediaIte * @param mediaItems [DAO] used to perform media item lookups. */ fun toTarget(mediaItems: DAO) = when(this.type){ - TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TaskDescriptionTarget.VideoSegmentTarget(mediaItems[this.mediaItems.first().mediaItem.UID()]!! as MediaItem.VideoItem, this.mediaItems.first().temporalRange!!) - TaskType.TargetType.JUDGEMENT -> TaskDescriptionTarget.JudgementTaskDescriptionTarget(this.mediaItems.map { mediaItems[it.mediaItem.UID()]!! to it.temporalRange }) - TaskType.TargetType.SINGLE_MEDIA_ITEM -> TaskDescriptionTarget.MediaItemTarget(mediaItems[this.mediaItems.first().mediaItem.UID()]!!) - TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> TaskDescriptionTarget.MultipleMediaItemTarget(this.mediaItems.map { mediaItems[it.mediaItem.UID()]!! }) - TaskType.TargetType.VOTE -> TaskDescriptionTarget.VoteTaskDescriptionTarget(this.mediaItems.map { mediaItems[it.mediaItem.UID()]!! to it.temporalRange }) + TargetOption.SINGLE_MEDIA_SEGMENT -> TaskDescriptionTarget.VideoSegmentTarget(mediaItems[this.mediaItems.first().mediaItem.UID()]!! as MediaItem.VideoItem, this.mediaItems.first().temporalRange!!) + TargetOption.JUDGEMENT -> TaskDescriptionTarget.JudgementTaskDescriptionTarget(this.mediaItems.map { mediaItems[it.mediaItem.UID()]!! to it.temporalRange }) + TargetOption.SINGLE_MEDIA_ITEM -> TaskDescriptionTarget.MediaItemTarget(mediaItems[this.mediaItems.first().mediaItem.UID()]!!) + TargetOption.MULTIPLE_MEDIA_ITEMS -> TaskDescriptionTarget.MultipleMediaItemTarget(this.mediaItems.map { mediaItems[it.mediaItem.UID()]!! }) + TargetOption.VOTE -> TaskDescriptionTarget.VoteTaskDescriptionTarget(this.mediaItems.map { mediaItems[it.mediaItem.UID()]!! to it.temporalRange }) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt index 2e751556c..29761b3ec 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt @@ -58,7 +58,7 @@ class TaskDescription( * * @return [TaskScorer]. */ - override fun newScorer(): TaskScorer = taskType.newScorer() + override fun newScorer(): TaskScorer = this.taskType.newScorer() /** @@ -67,7 +67,7 @@ class TaskDescription( * * @return [SubmissionFilter] */ - override fun newFilter(): SubmissionFilter = taskType.newFilter() + override fun newFilter(): SubmissionFilter = this.taskType.newFilter() /** * Generates and returns a [TaskHint] object to be used by the RESTful interface. diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt index c61fdea35..993b557f9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt @@ -1,72 +1,31 @@ package dev.dres.data.model.competition +import dev.dres.data.model.competition.options.* import dev.dres.run.filter.* import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.KisTaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator -interface Option { - val ordinal: Int -} -data class ConfiguredOption(val option: T, val parameters : Map = emptyMap()) data class TaskType( - val name: String, - val taskDuration: Long, //in ms - val targetType: ConfiguredOption, - val components: Collection>, - val score: ConfiguredOption, - val filter: Collection>, - val options: Collection> + val name: String, + val taskDuration: Long, //in ms + val targetType: ConfiguredOption, + val components: Collection>, + val score: ConfiguredOption, + val filter: Collection>, + val options: Collection> ) { - enum class Options : Option{ - HIDDEN_RESULTS, //Do not show submissions while task is running - MAP_TO_SEGMENT //Map the time of a submission to a pre-defined segment - } - - enum class TargetType : Option{ - SINGLE_MEDIA_ITEM, // Whole Media Item" - SINGLE_MEDIA_SEGMENT, //Part of a Media Item - MULTIPLE_MEDIA_ITEMS, //Multiple Media Items - JUDGEMENT, //Judgement - VOTE //Judgement with audience voting - } - - enum class QueryComponentType : Option{ - IMAGE_ITEM, //Image Media Item - VIDEO_ITEM_SEGMENT, //Part of a Video Media Item - TEXT, - EXTERNAL_IMAGE, - EXTERNAL_VIDEO - } - - enum class ScoringType : Option{ - KIS, - AVS - } - - enum class SubmissionFilterType(internal val filter: (parameters: Map) -> SubmissionFilter) : Option{ - NO_DUPLICATES({_ -> DuplicateSubmissionFilter()}), - LIMIT_CORRECT_PER_TEAM({params -> CorrectSubmissionPerTeamFilter(params)}), - LIMIT_WRONG_PER_TEAM({params -> MaximumWrongSubmissionsPerTeam(params)}), - LIMIT_TOTAL_PER_TEAM({params -> MaximumTotalSubmissionsPerTeam(params)}), - LIMIT_CORRECT_PER_MEMBER({params -> CorrectSubmissionPerTeamMemberFilter(params)}), - TEMPORAL_SUBMISSION({_ ->TemporalSubmissionFilter()}) - } - /** * Generates a new [TaskScorer] for this [TaskDescription]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * @return [TaskScorer]. */ - fun newScorer(): TaskScorer = when(score.option){ - ScoringType.KIS -> KisTaskScorer(score.parameters) - ScoringType.AVS -> AvsTaskScorer() - } + fun newScorer(): TaskScorer = this.score.option.scorer(this.score.parameters) /** * Generates and returns a [SubmissionValidator] instance for this [TaskDescription]. Depending @@ -75,11 +34,9 @@ data class TaskType( * @return [SubmissionFilter] */ fun newFilter(): SubmissionFilter { - if (filter.isEmpty()){ + if (this.filter.isEmpty()) { return AllSubmissionFilter } - - return filter.map { it.option.filter(it.parameters) }.reduceRight(SubmissionFilter::and) + return this.filter.map { it.option.filter(it.parameters) }.reduceRight(SubmissionFilter::and) } - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt new file mode 100644 index 000000000..9f7d341b2 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt @@ -0,0 +1,49 @@ +package dev.dres.data.model.competition.options + +/** + * A concrete instance for an [Option] including named paramters. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.1.0 + */ +data class ConfiguredOption(val option: T, val parameters : Map = emptyMap()) { + /** + * Tries to access the named parameter. Returns null, if the parameter is not set. + * + * @param name Name of the parameter to return. + * @return [String] or null. + */ + operator fun get(name: String): String? = this.parameters[name] + + /** + * Tries to parse a named parameter as [Int]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Int] or null. + */ + fun getAsInt(name: String): Int? = this.parameters[name]?.toIntOrNull() + + /** + * Tries to parse a named parameter as [Long]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Long] or null. + */ + fun getAsLong(name: String): Long? = this.parameters[name]?.toLongOrNull() + + /** + * Tries to parse a named parameter as [Float]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Float] or null. + */ + fun getAsFloat(name: String): Float? = this.parameters[name]?.toFloatOrNull() + + /** + * Tries to parse a named parameter as [Double]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Double] or null. + */ + fun getAsDouble(name: String): Double? = this.parameters[name]?.toDoubleOrNull() +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/Option.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/Option.kt new file mode 100644 index 000000000..17d6e6cf9 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/Option.kt @@ -0,0 +1,10 @@ +package dev.dres.data.model.competition.options + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +interface Option { + val ordinal: Int +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/QueryComponentOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/QueryComponentOption.kt new file mode 100644 index 000000000..f0822bd8a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/QueryComponentOption.kt @@ -0,0 +1,14 @@ +package dev.dres.data.model.competition.options + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +enum class QueryComponentOption: Option { + IMAGE_ITEM, //Image Media Item + VIDEO_ITEM_SEGMENT, //Part of a Video Media Item + TEXT, + EXTERNAL_IMAGE, + EXTERNAL_VIDEO +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ScoringOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ScoringOption.kt new file mode 100644 index 000000000..7639ab456 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ScoringOption.kt @@ -0,0 +1,25 @@ +package dev.dres.data.model.competition.options + +import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.AvsTaskScorer +import dev.dres.run.score.scorer.KisTaskScorer + +/** + * An [Option] to specify the different types of [TaskScorer]s. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ScoringOption: Option { + KIS, AVS; + + /** + * Returns the [TaskScorer] for this [ScoringOption]. + * + * @param parameters The parameter [Map] used to configure the [TaskScorer] + */ + fun scorer(parameters: Map): TaskScorer = when(this) { + KIS -> KisTaskScorer(parameters) + AVS -> AvsTaskScorer() + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOption.kt new file mode 100644 index 000000000..3fb79e6a3 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOption.kt @@ -0,0 +1,13 @@ +package dev.dres.data.model.competition.options + +/** + * Simple [Option]s that can be applied to a task. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.1.0 + */ +enum class SimpleOption: Option { + HIDDEN_RESULTS, /** Do not show submissions while task is running. */ + MAP_TO_SEGMENT, /** Map the time of a submission to a pre-defined segment. */ + PROLONG_ON_SUBMISSION; /** Prolongs a task if a submission arrives within a certain time limit towards the end. */ +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOptionParameters.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOptionParameters.kt new file mode 100644 index 000000000..b99ce6f0e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOptionParameters.kt @@ -0,0 +1,31 @@ +package dev.dres.data.model.competition.options + +/** + * Named parameters and defaults for [SimpleOption]s. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +object SimpleOptionParameters { + /** + * Key for [SimpleOption.PROLONG_ON_SUBMISSION]; [Int] value expected. The parameter determines how many milliseconds + * before the end of a task a [SimpleOption.PROLONG_ON_SUBMISSION] [Option] should be applied. If a task has a total duration + * of 500s, then a value of 5s means that submissions arriving between 495s and 500s may trigger prolongation of the task. + */ + const val PROLONG_ON_SUBMISSION_LIMIT_PARAM = "limit" + + /** Default value for [PROLONG_ON_SUBMISSION_LIMIT_PARAM]. Defaults to the rule being applied during the final 5 seconds. */ + const val PROLONG_ON_SUBMISSION_LIMIT_DEFAULT = 5 + + /** Key for [SimpleOption.PROLONG_ON_SUBMISSION]; [Int] value expected. Determines by how many seconds a task should be prolonged. */ + const val PROLONG_ON_SUBMISSION_BY_PARAM = "prolong_by" + + /** Default value for [PROLONG_ON_SUBMISSION_BY_PARAM]. Defaults to a prolongation of 5 seconds. */ + const val PROLONG_ON_SUBMISSION_BY_DEFAULT = 5 + + /** Key for [SimpleOption.PROLONG_ON_SUBMISSION]; [Int] value expected. Determines by how many seconds a task should be prolonged. */ + const val PROLONG_ON_SUBMISSION_CORRECT_PARAM = "correct_only" + + /** Default value for [PROLONG_ON_SUBMISSION_BY_PARAM]. Defaults to a prolongation of 5 seconds. */ + const val PROLONG_ON_SUBMISSION_CORRECT_DEFAULT = false +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt new file mode 100644 index 000000000..7fde66de1 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt @@ -0,0 +1,17 @@ +package dev.dres.data.model.competition.options + +import dev.dres.run.filter.* + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +enum class SubmissionFilterOption(internal val filter: (parameters: Map) -> SubmissionFilter) : Option { + NO_DUPLICATES({_ -> DuplicateSubmissionFilter() }), + LIMIT_CORRECT_PER_TEAM({params -> CorrectSubmissionPerTeamFilter(params) }), + LIMIT_WRONG_PER_TEAM({params -> MaximumWrongSubmissionsPerTeam(params) }), + LIMIT_TOTAL_PER_TEAM({params -> MaximumTotalSubmissionsPerTeam(params) }), + LIMIT_CORRECT_PER_MEMBER({params -> CorrectSubmissionPerTeamMemberFilter(params) }), + TEMPORAL_SUBMISSION({_ -> TemporalSubmissionFilter() }) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/TargetOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/TargetOption.kt new file mode 100644 index 000000000..51185b930 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/TargetOption.kt @@ -0,0 +1,14 @@ +package dev.dres.data.model.competition.options + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +enum class TargetOption : Option { + SINGLE_MEDIA_ITEM, // Whole Media Item" + SINGLE_MEDIA_SEGMENT, //Part of a Media Item + MULTIPLE_MEDIA_ITEMS, //Multiple Media Items + JUDGEMENT, //Judgement + VOTE //Judgement with audience voting +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index 1459475a8..a33162139 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -4,6 +4,7 @@ import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.TaskDescriptionTarget import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.competition.options.TargetOption import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter @@ -42,10 +43,10 @@ abstract class AbstractInteractiveTask: AbstractRun(), Task { * @return [SubmissionValidator]. */ internal fun newValidator(): SubmissionValidator = when(description.taskType.targetType.option){ - TaskType.TargetType.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionValidator(setOf((description.target as TaskDescriptionTarget.MediaItemTarget).item)) - TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionValidator(description.target as TaskDescriptionTarget.VideoSegmentTarget) - TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionValidator((description.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) - TaskType.TargetType.JUDGEMENT -> BasicJudgementValidator(knownCorrectRanges = + TargetOption.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionValidator(setOf((description.target as TaskDescriptionTarget.MediaItemTarget).item)) + TargetOption.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionValidator(description.target as TaskDescriptionTarget.VideoSegmentTarget) + TargetOption.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionValidator((description.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) + TargetOption.JUDGEMENT -> BasicJudgementValidator(knownCorrectRanges = (description.target as TaskDescriptionTarget.JudgementTaskDescriptionTarget).targets.map { if (it.second == null) { ItemRange(it.first) @@ -58,7 +59,7 @@ abstract class AbstractInteractiveTask: AbstractRun(), Task { } ItemRange(item, range.first, range.second) } }) - TaskType.TargetType.VOTE -> BasicVoteValidator( + TargetOption.VOTE -> BasicVoteValidator( knownCorrectRanges = (description.target as TaskDescriptionTarget.VoteTaskDescriptionTarget).targets.map { if (it.second == null) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index 61aa3a500..f47798a40 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -2,6 +2,7 @@ package dev.dres.data.model.run import dev.dres.data.model.competition.TaskDescriptionTarget import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.competition.options.TargetOption import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.aspects.OriginAspect import dev.dres.data.model.submissions.batch.ResultBatch @@ -21,11 +22,11 @@ abstract class AbstractNonInteractiveTask: AbstractRun(), Task { val validator: SubmissionBatchValidator = newValidator() internal fun newValidator(): SubmissionBatchValidator = when(this.description.taskType.targetType.option){ - TaskType.TargetType.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionBatchValidator(setOf((description.target as TaskDescriptionTarget.MediaItemTarget).item)) - TaskType.TargetType.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionBatchValidator(description.target as TaskDescriptionTarget.VideoSegmentTarget) - TaskType.TargetType.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionBatchValidator((description.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) - TaskType.TargetType.JUDGEMENT -> TODO() - TaskType.TargetType.VOTE -> TODO() + TargetOption.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionBatchValidator(setOf((description.target as TaskDescriptionTarget.MediaItemTarget).item)) + TargetOption.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionBatchValidator(description.target as TaskDescriptionTarget.VideoSegmentTarget) + TargetOption.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionBatchValidator((description.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) + TargetOption.JUDGEMENT -> TODO() + TargetOption.VOTE -> TODO() } abstract fun addSubmissionBatch(origin: OriginAspect, batches: List>) diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TaskTypeSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TaskTypeSerializer.kt index e3f4e098b..5e1d32f03 100644 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TaskTypeSerializer.kt +++ b/backend/src/main/kotlin/dev/dres/data/serializers/TaskTypeSerializer.kt @@ -1,7 +1,7 @@ package dev.dres.data.serializers -import dev.dres.data.model.competition.ConfiguredOption import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.competition.options.* import org.mapdb.DataInput2 import org.mapdb.DataOutput2 import org.mapdb.Serializer @@ -36,11 +36,11 @@ object TaskTypeSerializer : Serializer { TaskType( input.readUTF(), input.unpackLong(), - ConfiguredOption(TaskType.TargetType.values()[input.unpackInt()], deserializeMap(input)), - (0 until input.unpackInt()).map { ConfiguredOption(TaskType.QueryComponentType.values()[input.unpackInt()], deserializeMap(input)) }, - ConfiguredOption(TaskType.ScoringType.values()[input.unpackInt()], deserializeMap(input)), - (0 until input.unpackInt()).map { ConfiguredOption(TaskType.SubmissionFilterType.values()[input.unpackInt()], deserializeMap(input)) }, - (0 until input.unpackInt()).map { ConfiguredOption(TaskType.Options.values()[input.unpackInt()], deserializeMap(input)) } + ConfiguredOption(TargetOption.values()[input.unpackInt()], deserializeMap(input)), + (0 until input.unpackInt()).map { ConfiguredOption(QueryComponentOption.values()[input.unpackInt()], deserializeMap(input)) }, + ConfiguredOption(ScoringOption.values()[input.unpackInt()], deserializeMap(input)), + (0 until input.unpackInt()).map { ConfiguredOption(SubmissionFilterOption.values()[input.unpackInt()], deserializeMap(input)) }, + (0 until input.unpackInt()).map { ConfiguredOption(SimpleOption.values()[input.unpackInt()], deserializeMap(input)) } ) diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 861f1cb01..7f1301558 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -1,6 +1,6 @@ package dev.dres.run.updatables -import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.competition.options.SubmissionFilterOption import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.InteractiveRunManager @@ -17,8 +17,8 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte override fun update(status: RunManagerStatus) { val taskRun = this.run.currentTask(this.context) if (taskRun != null) { - val limitingFilter = taskRun.description.taskType.filter.find{ it.option == TaskType.SubmissionFilterType.LIMIT_CORRECT_PER_TEAM } ?: return - val limit = limitingFilter.parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1 + val limitingFilter = taskRun.description.taskType.filter.find{ it.option == SubmissionFilterOption.LIMIT_CORRECT_PER_TEAM } ?: return + val limit = limitingFilter.getAsInt("limit") ?: 1 if (this.run.timeLeft(context) > 0) { if (this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { /* Determine of all teams have submitted . */ diff --git a/backend/src/main/resources/html/index.html b/backend/src/main/resources/html/index.html index e69de29bb..0519ecba6 100644 --- a/backend/src/main/resources/html/index.html +++ b/backend/src/main/resources/html/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index 0f4c5bb1a..d5830780d 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionOptions":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionQueryComponentType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetType":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetType"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentType"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringType"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterType"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionOptions"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/tasks/csv":{"get":{"summary":"Get api score run with runid tasks csv","operationId":"getApiScoreRunWithRunidTasksCsv","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Default response"}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionQueryComponentOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSimpleOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT","PROLONG_ON_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetOption"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentOption"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringOption"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterOption"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSimpleOption"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 9d7b6632a..c5bfc11db 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,7 @@ "@angular/router": "~9.1.0", "@openapitools/openapi-generator-cli": "^1.0.15-4.3.1", "angularx-qrcode": "^11.0.0", - "apexcharts": "^3.19.2", + "apexcharts": "^3.25.0", "hammerjs": "^2.0.8", "ng-apexcharts": "^1.3.0", "ng5-slider": "^1.2.4", diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index f602106a7..ab09307da 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -1,9 +1,8 @@ import {Component, ElementRef, Inject, ViewChild} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material/dialog'; import { - CollectionService, - ConfiguredOptionQueryComponentType, - ConfiguredOptionTargetType, + CollectionService, ConfiguredOptionQueryComponentOption, + ConfiguredOptionTargetOption, RestMediaCollection, RestMediaItem, RestTaskDescription, @@ -18,11 +17,9 @@ import {filter, first} from 'rxjs/operators'; import {AppConfig} from '../../../app.config'; import {CompetitionFormBuilder} from './competition-form.builder'; import { - VideoPlayerSegmentBuilderComponent, VideoPlayerSegmentBuilderData } from './video-player-segment-builder/video-player-segment-builder.component'; import {AdvancedBuilderDialogComponent, AdvancedBuilderDialogData} from './advanced-builder-dialog/advanced-builder-dialog.component'; -import {VideoPlayerSegmentBuilderDialogComponent} from './video-player-segment-builder-dialog/video-player-segment-builder-dialog.component'; /** @@ -94,7 +91,7 @@ export class CompetitionBuilderTaskDialogComponent { /** * Handler for (+) button for query target form component. */ - public addQueryTarget(targetType: ConfiguredOptionTargetType.OptionEnum) { + public addQueryTarget(targetType: ConfiguredOptionTargetOption.OptionEnum) { this.builder.addTargetForm(targetType); } @@ -110,7 +107,7 @@ export class CompetitionBuilderTaskDialogComponent { /** * Handler for (+) button for query hint form component. */ - public addQueryComponent(componentType: ConfiguredOptionQueryComponentType.OptionEnum) { + public addQueryComponent(componentType: ConfiguredOptionQueryComponentOption.OptionEnum) { this.builder.addComponentForm(componentType); } @@ -276,7 +273,7 @@ export class CompetitionBuilderTaskDialogComponent { this.builder.removeTargetForm(0); const mediaCollectionId = this.builder.form.get('mediaCollection').value; r.forEach((name, idx) => { - const form = this.builder.addTargetForm(ConfiguredOptionTargetType.OptionEnum.MULTIPLE_MEDIA_ITEMS); + const form = this.builder.addTargetForm(ConfiguredOptionTargetOption.OptionEnum.MULTIPLE_MEDIA_ITEMS); console.log(`${mediaCollectionId} ? ${name}`); const nameNoExt = name.substring(0, name.lastIndexOf('.')); this.collectionService.getApiCollectionWithCollectionidWithStartswith(mediaCollectionId, nameNoExt) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index a1fcbdcd2..244d2cbbd 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -1,5 +1,5 @@ import { - CollectionService, ConfiguredOptionQueryComponentType, ConfiguredOptionTargetType, + CollectionService, ConfiguredOptionQueryComponentOption, ConfiguredOptionTargetOption, RestMediaItem, RestTaskDescription, RestTaskDescriptionComponent, @@ -51,7 +51,7 @@ export class CompetitionFormBuilder { * * @param type The {@link ConfiguredOptionQueryComponentType.OptionEnum} to add a {@link FormGroup} for. */ - public addComponentForm(type: ConfiguredOptionQueryComponentType.OptionEnum) { + public addComponentForm(type: ConfiguredOptionQueryComponentOption.OptionEnum) { const array = this.form.get('components') as FormArray; const newIndex = array.length; switch (type) { @@ -78,7 +78,7 @@ export class CompetitionFormBuilder { * * @param type The {@link TaskType.TargetTypeEnum} to add a {@link FormGroup} for. */ - public addTargetForm(type: ConfiguredOptionTargetType.OptionEnum) { + public addTargetForm(type: ConfiguredOptionTargetOption.OptionEnum) { const array = this.form.get('target') as FormArray; const newIndex = array.length; switch (type) { diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts index a7d06f042..84fc62c5e 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts @@ -1,12 +1,11 @@ import {AfterViewInit, Component, Inject, OnInit} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import { - ConfiguredOptionOptions, - ConfiguredOptionQueryComponentType, - ConfiguredOptionScoringType, - ConfiguredOptionSubmissionFilterType, - ConfiguredOptionTargetType, - TaskGroup, + ConfiguredOptionQueryComponentOption, + ConfiguredOptionScoringOption, + ConfiguredOptionSimpleOption, + ConfiguredOptionSubmissionFilterOption, + ConfiguredOptionTargetOption, TaskType } from '../../../../../openapi'; import {FormArray, FormControl, FormGroup, Validators} from '@angular/forms'; @@ -35,22 +34,22 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV * Dynamically generated list of all target types. Since TargetType is an enum, values is required as this is the "underscore sensitive" * version. Object.keys() strips the underscores from the names. */ - targetTypes = Object.values(ConfiguredOptionTargetType.OptionEnum).sort((a, b) => a.localeCompare(b)); // sorted alphabetically - componentTypes = Object.values(ConfiguredOptionQueryComponentType.OptionEnum) + targetTypes = Object.values(ConfiguredOptionTargetOption.OptionEnum).sort((a, b) => a.localeCompare(b)); // sorted alphabetically + componentTypes = Object.values(ConfiguredOptionQueryComponentOption.OptionEnum) .sort((a, b) => a.localeCompare(b)) .map((v) => { - return {type: v, activated: false} as ActivatedType; + return {type: v, activated: false} as ActivatedType; }); - scoreTypes = Object.values(ConfiguredOptionScoringType.OptionEnum).sort((a, b) => a.localeCompare(b)); - filterTypes = Object.values(ConfiguredOptionSubmissionFilterType.OptionEnum) + scoreTypes = Object.values(ConfiguredOptionScoringOption.OptionEnum).sort((a, b) => a.localeCompare(b)); + filterTypes = Object.values(ConfiguredOptionSubmissionFilterOption.OptionEnum) .sort((a, b) => a.localeCompare(b)) .map((v) => { - return {type: v, activated: false} as ActivatedType; + return {type: v, activated: false} as ActivatedType; }); - options = Object.values(ConfiguredOptionOptions.OptionEnum) + options = Object.values(ConfiguredOptionSimpleOption.OptionEnum) .sort((a, b) => a.localeCompare(b)) .map((v) => { - return {type: v, activated: false} as ActivatedType; + return {type: v, activated: false} as ActivatedType; }); constructor( @@ -243,20 +242,20 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV targetType: { option: this.form.get('target').value, parameters: this.fetchConfigurationParameters(this.form.get('scoring').value) - } as ConfiguredOptionTargetType, + } as ConfiguredOptionTargetOption, components: (this.form.get('components') as FormArray).controls.map(c => { return {option: c.value, parameters: this.fetchConfigurationParameters(c.value)}; - }) as Array, + }) as Array, score: { option: this.form.get('scoring').value, parameters: this.fetchConfigurationParameters(this.form.get('scoring').value) - } as ConfiguredOptionScoringType, + } as ConfiguredOptionScoringOption, filter: (this.form.get('filters') as FormArray).controls.map(c => { return {option: c.value, parameters: this.fetchConfigurationParameters(c.value)}; - }) as Array, + }) as Array, options: (this.form.get('options') as FormArray).controls.map(c => { return {option: c.value, parameters: this.fetchConfigurationParameters(c.value)}; - }) as Array + }) as Array } as TaskType; } From 25d6f1604eca0d5e3af6fef873681feccf5db80d Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 7 Mar 2021 22:15:09 +0100 Subject: [PATCH 53/95] Prepared CompetitionBuilderComponent for new option and fixed removal of task types and task group. In addition to fixing a bug, removing a task type or task group should now also cause dependent task types and task groups to be removed. --- .../competition-builder.component.ts | 117 ++++++++++-------- 1 file changed, 62 insertions(+), 55 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index f5e4db55f..f0fb7de5d 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -3,11 +3,11 @@ import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router'; import {filter} from 'rxjs/operators'; import { CompetitionService, - ConfiguredOptionOptions, - ConfiguredOptionQueryComponentType, - ConfiguredOptionScoringType, - ConfiguredOptionSubmissionFilterType, - ConfiguredOptionTargetType, + ConfiguredOptionQueryComponentOption, + ConfiguredOptionScoringOption, + ConfiguredOptionSimpleOption, + ConfiguredOptionSubmissionFilterOption, + ConfiguredOptionTargetOption, RestCompetitionDescription, RestTaskDescription, RestTeam, @@ -45,18 +45,18 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat public static TKIS_TEMPLATE = { name: 'Textual KIS', taskDuration: 420, - targetType: {option: ConfiguredOptionTargetType.OptionEnum.SINGLE_MEDIA_SEGMENT, parameters: {}}, - score: {option: ConfiguredOptionScoringType.OptionEnum.KIS, parameters: {}}, + targetType: {option: ConfiguredOptionTargetOption.OptionEnum.SINGLE_MEDIA_SEGMENT, parameters: {}}, + score: {option: ConfiguredOptionScoringOption.OptionEnum.KIS, parameters: {}}, components: [ - {option: ConfiguredOptionQueryComponentType.OptionEnum.TEXT, parameters: {}} + {option: ConfiguredOptionQueryComponentOption.OptionEnum.TEXT, parameters: {}} ], filter: [ - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.NO_DUPLICATES, parameters: {}}, - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.LIMIT_CORRECT_PER_TEAM, parameters: {limit: 1}}, - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.TEMPORAL_SUBMISSION, parameters: {}} + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.NO_DUPLICATES, parameters: {}}, + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.LIMIT_CORRECT_PER_TEAM, parameters: {limit: 1}}, + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.TEMPORAL_SUBMISSION, parameters: {}} ], options: [ - {option: ConfiguredOptionOptions.OptionEnum.HIDDEN_RESULTS, parameters: {}}, + {option: ConfiguredOptionSimpleOption.OptionEnum.HIDDEN_RESULTS, parameters: {}}, ] } as TaskType; @@ -66,15 +66,15 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat public static VKIS_TEMPLATE = { name: 'Visual KIS', taskDuration: 300, - targetType: {option: ConfiguredOptionTargetType.OptionEnum.SINGLE_MEDIA_SEGMENT, parameters: {}}, - score: {option: ConfiguredOptionScoringType.OptionEnum.KIS, parameters: {}}, + targetType: {option: ConfiguredOptionTargetOption.OptionEnum.SINGLE_MEDIA_SEGMENT, parameters: {}}, + score: {option: ConfiguredOptionScoringOption.OptionEnum.KIS, parameters: {}}, components: [ - {option: ConfiguredOptionQueryComponentType.OptionEnum.VIDEO_ITEM_SEGMENT, parameters: {}} + {option: ConfiguredOptionQueryComponentOption.OptionEnum.VIDEO_ITEM_SEGMENT, parameters: {}} ], filter: [ - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.NO_DUPLICATES, parameters: {}}, - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.LIMIT_CORRECT_PER_TEAM, parameters: {limit: 1}}, - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.TEMPORAL_SUBMISSION, parameters: {}} + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.NO_DUPLICATES, parameters: {}}, + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.LIMIT_CORRECT_PER_TEAM, parameters: {limit: 1}}, + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.TEMPORAL_SUBMISSION, parameters: {}} ], options: [] } as TaskType; @@ -85,17 +85,17 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat public static AVS_TEMPLATE = { name: 'Ad-hoc Video Search', taskDuration: 300, - targetType: {option: ConfiguredOptionTargetType.OptionEnum.JUDGEMENT, parameters: {}}, - score: {option: ConfiguredOptionScoringType.OptionEnum.AVS, parameters: {}}, + targetType: {option: ConfiguredOptionTargetOption.OptionEnum.JUDGEMENT, parameters: {}}, + score: {option: ConfiguredOptionScoringOption.OptionEnum.AVS, parameters: {}}, components: [ - {option: ConfiguredOptionQueryComponentType.OptionEnum.TEXT, parameters: {}} + {option: ConfiguredOptionQueryComponentOption.OptionEnum.TEXT, parameters: {}} ], filter: [ - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.NO_DUPLICATES, parameters: {limit: 1}}, - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.TEMPORAL_SUBMISSION, parameters: {}} + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.NO_DUPLICATES, parameters: {limit: 1}}, + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.TEMPORAL_SUBMISSION, parameters: {}} ], options: [ - {option: ConfiguredOptionOptions.OptionEnum.MAP_TO_SEGMENT, parameters: {}} + {option: ConfiguredOptionSimpleOption.OptionEnum.MAP_TO_SEGMENT, parameters: {}} ] } as TaskType; @@ -105,17 +105,17 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat public static LSC_TEMPLATE = { name: 'LSC', taskDuration: 300, - targetType: {option: ConfiguredOptionTargetType.OptionEnum.MULTIPLE_MEDIA_ITEMS, parameters: {}}, - score: {option: ConfiguredOptionScoringType.OptionEnum.KIS, parameters: {}}, + targetType: {option: ConfiguredOptionTargetOption.OptionEnum.MULTIPLE_MEDIA_ITEMS, parameters: {}}, + score: {option: ConfiguredOptionScoringOption.OptionEnum.KIS, parameters: {}}, components: [ - {option: ConfiguredOptionQueryComponentType.OptionEnum.TEXT, parameters: {}} + {option: ConfiguredOptionQueryComponentOption.OptionEnum.TEXT, parameters: {}} ], filter: [ - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.NO_DUPLICATES, parameters: {}}, - {option: ConfiguredOptionSubmissionFilterType.OptionEnum.LIMIT_CORRECT_PER_TEAM, parameters: {}} + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.NO_DUPLICATES, parameters: {}}, + {option: ConfiguredOptionSubmissionFilterOption.OptionEnum.LIMIT_CORRECT_PER_TEAM, parameters: {}} ], options: [ - {option: ConfiguredOptionOptions.OptionEnum.HIDDEN_RESULTS, parameters: {}} + {option: ConfiguredOptionSimpleOption.OptionEnum.HIDDEN_RESULTS, parameters: {}} ] } as TaskType; @@ -227,6 +227,32 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat } + public addTaskType(type?: TaskType) { + const dialogRef = this.dialog.open( + CompetitionBuilderTaskTypeDialogComponent, + {data: type ? type : null, width: '750px'} + ); + dialogRef.afterClosed().pipe( + filter(g => g != null), + ).subscribe((g) => { + this.competition.taskTypes.push(g); + this.dirty = true; + }); + } + + /** + * Removes a task type and all associated task groups. + * + * @param taskType The {@link TaskType} to remove. + */ + removeTaskType(taskType: TaskType) { + this.competition.taskTypes.splice(this.competition.taskTypes.indexOf(taskType), 1); + this.competition.taskGroups.filter(t => t.type === taskType.name).forEach(g => { + this.removeTaskGroup(g); + }); + this.dirty = true; + } + /** * Opens the dialog to add a new task group. */ @@ -246,27 +272,15 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat }); } - public addTaskType(type?: TaskType) { - const dialogRef = this.dialog.open( - CompetitionBuilderTaskTypeDialogComponent, - {data: type ? type : null, width: '750px'} - ); - dialogRef.afterClosed().pipe( - filter(g => g != null), - ).subscribe((g) => { - this.competition.taskTypes.push(g); - this.dirty = true; - }); - } - /** - * Removes a Task Group and all associated tasks. + * Removes a task group and all associated tasks. + * + * @param group The {@link TaskGroup} to remove */ public removeTaskGroup(group: TaskGroup) { - this.competition.taskGroups.splice(this.competition.taskGroups.indexOf(group)); - // assuming taskGroup:string in task is the actual name --> late will be replaced by uuid - this.competition.tasks.filter(t => t.taskGroup === group.name).map(t => this.competition.tasks.indexOf(t)).forEach(i => { - this.competition.tasks.splice(i); + this.competition.taskGroups.splice(this.competition.taskGroups.indexOf(group), 1); + this.competition.tasks.filter(t => t.taskGroup === group.name).forEach(t => { + this.removeTask(t); }); this.dirty = true; } @@ -278,7 +292,6 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat */ public addTask(group: TaskGroup) { const type = this.competition.taskTypes.find(v => v.name === group.type); - // const width = window.screen.width * .75; const width = 750; const dialogRef = this.dialog.open( CompetitionBuilderTaskDialogComponent, @@ -300,7 +313,6 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat */ public editTask(task: RestTaskDescription) { const index = this.competition.tasks.indexOf(task); - // const width = window.screen.width * .75; const width = 750; if (index > -1) { const dialogRef = this.dialog.open( @@ -378,11 +390,6 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat this.teamTable.renderRows(); } - removeTaskType(taskType: TaskType) { - this.competition.taskTypes.splice(this.competition.taskTypes.indexOf(taskType), 1); - this.dirty = true; - } - /** * Summarises a task type to present detailed info as tooltip. * From 6b00ebe1a88c6dfc64a98f6ff1962f5aa3ed1839 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 7 Mar 2021 22:27:58 +0100 Subject: [PATCH 54/95] Added logic to implement the PROLONG_ON_SUBMIT option. Addresses #124 --- .../competition/options/ConfiguredOption.kt | 8 +++++ .../run/InteractiveSynchronousRunManager.kt | 35 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt index 9f7d341b2..7c280fb28 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt @@ -15,6 +15,14 @@ data class ConfiguredOption(val option: T, val parameters : Map, sub: Submission) { + val limit = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT + val prolongBy = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_BY_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_BY_DEFAULT + val correctOnly = option.getAsBool(SimpleOptionParameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT + if (correctOnly && sub.status != SubmissionStatus.CORRECT) { + return + } + val timeLeft = Math.floorDiv(this.timeLeft(context), 1000) + if (timeLeft in 0 until limit) { + this.adjustDuration(context, prolongBy) + } + } } \ No newline at end of file From 0ffc326e867fdcc735a4531d5ec494138bfc2cac Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 7 Mar 2021 22:50:22 +0100 Subject: [PATCH 55/95] Refactored submission filters. --- .../api/rest/handler/SubmissionHandler.kt | 6 ++-- .../options/SubmissionFilterOption.kt | 33 ++++++++++++++----- .../run/InteractiveAsynchronousCompetition.kt | 4 ++- .../run/InteractiveSynchronousCompetition.kt | 5 ++- .../run/InteractiveSynchronousRunManager.kt | 3 +- .../dres/run/filter/CorrectPerTeamFilter.kt | 9 +++++ .../run/filter/CorrectPerTeamMemberFilter.kt | 10 ++++++ .../run/filter/CorrectSubmissionFilter.kt | 14 -------- .../run/filter/DuplicateSubmissionFilter.kt | 10 ++---- .../run/filter/MaximumTotalPerTeamFilter.kt | 10 ++++++ .../run/filter/MaximumWrongPerTeamFilter.kt | 14 ++++++++ .../dres/run/filter/SubmissionCountFilter.kt | 14 -------- .../dev/dres/run/filter/SubmissionFilter.kt | 14 +++++++- .../run/filter/SubmissionRejectedException.kt | 11 +++++++ 14 files changed, 105 insertions(+), 52 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/CorrectSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/SubmissionCountFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index 0d0189e15..c97c24931 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -26,6 +26,7 @@ import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.SubmissionEvent +import dev.dres.run.filter.SubmissionRejectedException import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.TimeUtil import dev.dres.utilities.extensions.sessionId @@ -142,11 +143,10 @@ class SubmissionHandler (val collections: DAO, private val item val result = try { run.postSubmission(rac, submission) - } catch (e: IllegalArgumentException) { //is only thrown by submission filter TODO: nicer exception type - throw ErrorStatusException(208, "Submission rejected", ctx) + } catch (e: SubmissionRejectedException) { + throw ErrorStatusException(208, "Submission rejected.", ctx) } - AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, submission, LogEventSource.REST, ctx.sessionId(), ctx.req.remoteAddr) EventStreamProcessor.event(SubmissionEvent(ctx.sessionId(), run.id, run.currentTask(rac)?.uid, submission)) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt index 7fde66de1..a2aad1c8a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt @@ -3,15 +3,30 @@ package dev.dres.data.model.competition.options import dev.dres.run.filter.* /** + * [Option] that can be applied to a task. Configures [SubmissionFilter]s that should be used. * - * @author Ralph Gasser - * @version 1.0 + * @author Luca Rossetto & Ralph Gasser + * @version 1.1.0 */ -enum class SubmissionFilterOption(internal val filter: (parameters: Map) -> SubmissionFilter) : Option { - NO_DUPLICATES({_ -> DuplicateSubmissionFilter() }), - LIMIT_CORRECT_PER_TEAM({params -> CorrectSubmissionPerTeamFilter(params) }), - LIMIT_WRONG_PER_TEAM({params -> MaximumWrongSubmissionsPerTeam(params) }), - LIMIT_TOTAL_PER_TEAM({params -> MaximumTotalSubmissionsPerTeam(params) }), - LIMIT_CORRECT_PER_MEMBER({params -> CorrectSubmissionPerTeamMemberFilter(params) }), - TEMPORAL_SUBMISSION({_ -> TemporalSubmissionFilter() }) +enum class SubmissionFilterOption : Option { + NO_DUPLICATES, + LIMIT_CORRECT_PER_TEAM, + LIMIT_WRONG_PER_TEAM, + LIMIT_TOTAL_PER_TEAM, + LIMIT_CORRECT_PER_MEMBER, + TEMPORAL_SUBMISSION; + + /** + * Returns the [SubmissionFilter] for this [SubmissionFilterOption] and the given [parameters]. + * + * @param parameters The parameter [Map] used to configure the [SubmissionFilter] + */ + fun filter(parameters: Map) = when (this) { + NO_DUPLICATES -> DuplicateSubmissionFilter() + LIMIT_CORRECT_PER_TEAM -> CorrectPerTeamFilter(parameters) + LIMIT_WRONG_PER_TEAM -> MaximumWrongPerTeamFilter(parameters) + LIMIT_TOTAL_PER_TEAM -> MaximumTotalPerTeamFilter(parameters) + LIMIT_CORRECT_PER_MEMBER -> CorrectPerTeamMemberFilter(parameters) + TEMPORAL_SUBMISSION -> TemporalSubmissionFilter() + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index c6b3fa52c..c28930860 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -116,7 +116,9 @@ class InteractiveAsynchronousCompetition(override var id: CompetitionId, overrid override fun addSubmission(submission: Submission) { check(!this.isRunning) { "Task run '${this@InteractiveAsynchronousCompetition.name}.${this.position}' is currently not running." } check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task." } - check(this.filter.test(submission)) { "The provided submission $submission was rejected by the filter." } + + /* Execute submission filters. */ + this.filter.acceptOrThrow(submission) /* Process Submission. */ (this.submissions as MutableList).add(submission) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt index e25082512..f2071c8b9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt @@ -12,6 +12,7 @@ import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.filter.SubmissionRejectedException import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import java.util.* @@ -103,7 +104,9 @@ class InteractiveSynchronousCompetition(override var id: CompetitionId, override check(this@InteractiveSynchronousCompetition.description.teams.any { it.uid == submission.teamId }) { "Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousCompetition.name}." } - check(this.filter.test(submission)) { "The provided submission $submission was rejected by the filter." } + + /* Execute submission filters. */ + this.filter.acceptOrThrow(submission) /* Process Submission. */ (this.submissions as MutableList).add(submission) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 24fb28e5c..995a70e5c 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -604,10 +604,11 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio * Applies the [SimpleOption.PROLONG_ON_SUBMISSION] [Option]. * * @param context [RunActionContext] used for invocation. - * @param task The [Task] to check for presence of [Option]. + * @param option The [ConfiguredOption] option. * @param sub The [Submission] to apply the [Option] for. */ private fun prolongOnSubmit(context: RunActionContext, option: ConfiguredOption, sub: Submission) { + require(option.option == SimpleOption.PROLONG_ON_SUBMISSION) { "Cannot process ${option.option} in prolongOnSubmit()." } val limit = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT val prolongBy = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_BY_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_BY_DEFAULT val correctOnly = option.getAsBool(SimpleOptionParameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt new file mode 100644 index 000000000..76b211c85 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -0,0 +1,9 @@ +package dev.dres.run.filter + +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus + +class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { + constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) + override fun test(submission: Submission): Boolean = submission.task!!.submissions.count { it.status == SubmissionStatus.CORRECT && it.teamId == submission.teamId } < limit +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt new file mode 100644 index 000000000..bcb88bfa4 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -0,0 +1,10 @@ +package dev.dres.run.filter + +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus + + +class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter { + constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) + override fun test(submission: Submission): Boolean = submission.task!!.submissions.count { it.status == SubmissionStatus.CORRECT && it.teamId == submission.teamId && it.memberId == submission.memberId } < limit +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectSubmissionFilter.kt deleted file mode 100644 index 05dff6fd9..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectSubmissionFilter.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus - -class CorrectSubmissionPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { - constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) - override fun test(submission: Submission): Boolean = submission.task!!.submissions.count { it.status == SubmissionStatus.CORRECT && it.teamId == submission.teamId } < limit -} - -class CorrectSubmissionPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter { - constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) - override fun test(submission: Submission): Boolean = submission.task!!.submissions.count { it.status == SubmissionStatus.CORRECT && it.teamId == submission.teamId && it.memberId == submission.memberId } < limit -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index 8c18f9c53..ce00fa30e 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -5,18 +5,12 @@ import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect class DuplicateSubmissionFilter : SubmissionFilter { override fun test(submission: Submission): Boolean = submission.task!!.submissions.none { - - it.teamId == submission.teamId && - it.item == submission.item && - + it.teamId == submission.teamId && + it.item == submission.item && if(submission is TemporalSubmissionAspect && it is TemporalSubmissionAspect) { - //contains a previous submission... /*(*/(submission.start <= it.start && submission.end >= it.end) /*|| */ } else { true } - - //or is contained by a previous submission - /*(it.start <= submission.start && it.end!! >= submission.end!!))*/ } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt new file mode 100644 index 000000000..6d2b2a6fc --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -0,0 +1,10 @@ +package dev.dres.run.filter + +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus + +class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : SubmissionFilter { + constructor(parameters: Map) : this(parameters.getOrDefault("limit", "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) + override fun test(submission: Submission): Boolean = submission.task!!.submissions.filter { it.teamId == submission.teamId }.size < max +} + diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt new file mode 100644 index 000000000..3df6e932d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -0,0 +1,14 @@ +package dev.dres.run.filter + +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionStatus + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : SubmissionFilter { + constructor(parameters: Map) : this(parameters.getOrDefault("limit", "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) + override fun test(submission: Submission): Boolean = submission.task!!.submissions.filter { it.teamId == submission.teamId && it.status == SubmissionStatus.WRONG }.size < max +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionCountFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionCountFilter.kt deleted file mode 100644 index 65ff4c507..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionCountFilter.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus - -class MaximumTotalSubmissionsPerTeam(private val max: Int = Int.MAX_VALUE) : SubmissionFilter { - constructor(parameters: Map) : this(parameters.getOrDefault("limit", "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) - override fun test(submission: Submission): Boolean = submission.task!!.submissions.filter { it.teamId == submission.teamId }.size < max -} - -class MaximumWrongSubmissionsPerTeam(private val max: Int = Int.MAX_VALUE) : SubmissionFilter { - constructor(parameters: Map) : this(parameters.getOrDefault("limit", "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) - override fun test(submission: Submission): Boolean = submission.task!!.submissions.filter { it.teamId == submission.teamId && it.status == SubmissionStatus.WRONG }.size < max -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt index 8dcf5f14c..836fcb71c 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt @@ -8,10 +8,22 @@ import java.util.function.Predicate * by the [Submission] evaluation pipeline. * * @author Ralph Gasser - * @version 1.0.0 + * @version 1.1.0 */ fun interface SubmissionFilter : Predicate { override infix fun and(other: Predicate): SubmissionFilter = SubmissionFilter { s -> this@SubmissionFilter.test(s) && other.test(s) } override infix fun or(other: Predicate): SubmissionFilter = SubmissionFilter { s -> this@SubmissionFilter.test(s) || other.test(s) } operator fun not(): SubmissionFilter = SubmissionFilter { s -> !this@SubmissionFilter.test(s) } + + /** + * Tests the given [Submission] with this [SubmissionFilter] and throws a [SubmissionRejectedException], if the test fails. + * + * @param submission The [Submission] to check. + * @throws SubmissionRejectedException on failure + */ + fun acceptOrThrow(submission: Submission) { + if (!this.test(submission)) { + throw SubmissionRejectedException(submission) + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt new file mode 100644 index 000000000..eabe9d63a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt @@ -0,0 +1,11 @@ +package dev.dres.run.filter + +import dev.dres.data.model.submissions.Submission + +/** + * An exception that is thrown, when a [Submission] is rejected by a [SubmissionFilter]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class SubmissionRejectedException(s: Submission) : Throwable("Submission $s was rejected by filter.") \ No newline at end of file From ad03dbacc8812edd16b0d89f84e7f4039ad0c320 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 Mar 2021 13:26:56 +0100 Subject: [PATCH 56/95] There are now special exceptions for illegal RunManagerStatus and unknown TeamIds during processing of RUnManager implementations. Addresses #16 --- .../api/rest/handler/SubmissionHandler.kt | 33 +++++++++------ .../api/rest/types/status/SuccessStatus.kt | 2 +- .../status/SuccessfulSubmissionsStatus.kt | 5 +++ .../run/InteractiveAsynchronousRunManager.kt | 42 ++++++++++++++----- .../run/InteractiveSynchronousRunManager.kt | 41 +++++++++--------- .../main/kotlin/dev/dres/run/RunManager.kt | 1 - .../exceptions/IllegalRunStateException.kt | 13 ++++++ .../run/exceptions/IllegalTeamIdException.kt | 13 ++++++ doc/oas.json | 2 +- 9 files changed, 107 insertions(+), 45 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/exceptions/IllegalRunStateException.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index c97c24931..78e5aca86 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.RestApiRole 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.status.SuccessfulSubmissionsStatus import dev.dres.data.dbo.DAO import dev.dres.data.dbo.DaoIndexer import dev.dres.data.model.Config @@ -26,6 +26,8 @@ import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.SubmissionEvent +import dev.dres.run.exceptions.IllegalRunStateException +import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.TimeUtil @@ -39,7 +41,7 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -class SubmissionHandler (val collections: DAO, private val itemIndex: DaoIndexer>, private val segmentIndex: DaoIndexer, private val config: Config): GetRestHandler, AccessManagedRestHandler { +class SubmissionHandler (val collections: DAO, private val itemIndex: DaoIndexer>, private val segmentIndex: DaoIndexer, private val config: Config): GetRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(RestApiRole.PARTICIPANT) override val route = "submit" @@ -126,15 +128,15 @@ class SubmissionHandler (val collections: DAO, private val item ], tags = ["Submission"], responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("208", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("200", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), + OpenApiResponse("202", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("409", [OpenApiContent(ErrorStatus::class)]) + OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): SuccessStatus { + override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) val run = getActiveRun(userId, ctx) val time = System.currentTimeMillis() @@ -144,7 +146,11 @@ class SubmissionHandler (val collections: DAO, private val item val result = try { run.postSubmission(rac, submission) } catch (e: SubmissionRejectedException) { - throw ErrorStatusException(208, "Submission rejected.", ctx) + throw ErrorStatusException(412, "Submission rejected by submission filter.", ctx) + } catch (e: IllegalRunStateException) { + throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) + } catch (e: IllegalTeamIdException) { + throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) } AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, submission, LogEventSource.REST, ctx.sessionId(), ctx.req.remoteAddr) @@ -155,15 +161,18 @@ class SubmissionHandler (val collections: DAO, private val item } return when (result) { - SubmissionStatus.CORRECT -> SuccessStatus("Submission correct!") - SubmissionStatus.WRONG -> SuccessStatus("Submission incorrect! Try again") - SubmissionStatus.INDETERMINATE -> SuccessStatus("Submission received. Waiting for verdict!") - SubmissionStatus.UNDECIDABLE -> SuccessStatus("Submission undecidable. Try again!") + SubmissionStatus.CORRECT -> SuccessfulSubmissionsStatus(SubmissionStatus.CORRECT, "Submission correct!") + SubmissionStatus.WRONG -> SuccessfulSubmissionsStatus(SubmissionStatus.WRONG, "Submission incorrect! Try again") + SubmissionStatus.INDETERMINATE -> { + ctx.status(202) /* HTTP Accepted. */ + SuccessfulSubmissionsStatus(SubmissionStatus.INDETERMINATE, "Submission received. Waiting for verdict!") + } + SubmissionStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(SubmissionStatus.UNDECIDABLE,"Submission undecidable. Try again!") } } private fun generatePreview(submission: Submission) { - if (submission !is TemporalSubmissionAspect){ + if (submission !is TemporalSubmissionAspect) { return } val collection = collections[submission.item.collection] ?: return diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessStatus.kt index 4ceb0286f..62c4da38c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessStatus.kt @@ -1,3 +1,3 @@ package dev.dres.api.rest.types.status -data class SuccessStatus(val description: String):AbstractStatus(status = true) \ No newline at end of file +data class SuccessStatus(val description: String): AbstractStatus(status = true) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt new file mode 100644 index 000000000..266f424b0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt @@ -0,0 +1,5 @@ +package dev.dres.api.rest.types.status + +import dev.dres.data.model.submissions.SubmissionStatus + +data class SuccessfulSubmissionsStatus(val submission: SubmissionStatus, val description: String) : AbstractStatus(status = true) \ 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 e535784ba..162590faa 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -17,6 +17,8 @@ import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent +import dev.dres.run.exceptions.IllegalRunStateException +import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.* @@ -154,7 +156,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.CREATED] */ override fun start(context: RunActionContext) = this.stateLock.write { - check(this.status == RunManagerStatus.CREATED) { "SynchronizedRunManager is in status ${this.status} and cannot be started." } + checkGlobalStatus(RunManagerStatus.CREATED) if (context.isAdmin) { /* Start the run. */ this.run.start() @@ -184,7 +186,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun end(context: RunActionContext) { - check(this.status != RunManagerStatus.TERMINATED) { "SynchronizedRunManager is in status ${this.status} and cannot be terminated." } + checkGlobalStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE) if (context.isAdmin) { /* End the run. */ this.run.end() @@ -212,7 +214,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous */ override fun currentTaskDescription(context: RunActionContext): TaskDescription { require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager."} - return this.navigationMap[context.teamId] ?: throw IllegalStateException("Could not find active task description for team ${context.teamId}.") + return this.navigationMap[context.teamId] ?: throw IllegalTeamIdException(context.teamId) } /** @@ -271,7 +273,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous */ override fun goTo(context: RunActionContext, index: Int) = this.stateLock.write { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} - check(this.statusMap[context.teamId] == RunManagerStatus.ACTIVE || this.status == RunManagerStatus.TASK_ENDED) { "Run manager for team ${context.teamId} is in status ${this.status}. Tasks can therefore not be changed." } + checkTeamStatus(context.teamId, RunManagerStatus.ACTIVE, RunManagerStatus.TASK_ENDED) if (index >= 0 && index < this.description.tasks.size) { /* Update active task. */ @@ -303,11 +305,10 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous */ override fun startTask(context: RunActionContext) = this.stateLock.write { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} - check(this.statusMap[context.teamId] == RunManagerStatus.ACTIVE) { "Run manager for team ${context.teamId} is in status ${this.status}. New task cannot be started." } - val currentTask = this.navigationMap[context.teamId] - check(currentTask != null) { } + checkTeamStatus(context.teamId, RunManagerStatus.ACTIVE) /* Create task and update status. */ + val currentTask = this.navigationMap[context.teamId] ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") this.run.Task(teamId = context.teamId, descriptionId = this.navigationMap[context.teamId]!!.id) this.statusMap[context.teamId] = RunManagerStatus.PREPARING_TASK @@ -331,9 +332,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous */ override fun abortTask(context: RunActionContext) = this.stateLock.write { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager."} - check((this.statusMap[context.teamId] == RunManagerStatus.PREPARING_TASK || this.statusMap[context.teamId] == RunManagerStatus.RUNNING_TASK)) { - "Run manager for team ${context.teamId} is in status ${this.status}. New task cannot be started." - } + checkTeamStatus(context.teamId, RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK) /* End TaskRun and update status. */ val currentTask = this.currentTask(context) ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") @@ -456,7 +455,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous */ override fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - check(this.statusMap[context.teamId] == RunManagerStatus.RUNNING_TASK) { "Run manager is in status ${this.status} and can currently not accept submissions." } + checkTeamStatus(context.teamId, RunManagerStatus.RUNNING_TASK) /* Register submission. */ val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") @@ -615,4 +614,25 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous } } } + + /** + * Checks if the [InteractiveAsynchronousRunManager] is in one of the given [RunManagerStatus] and throws an exception, if not. + * + * @param status List of expected [RunManagerStatus]. + */ + private fun checkGlobalStatus(vararg status: RunManagerStatus) { + if (this.status !in status) throw IllegalRunStateException(this.status) + } + + /** + * Checks if the team status for the given [TeamId] and this [InteractiveAsynchronousRunManager] + * is in one of the given [RunManagerStatus] and throws an exception, if not. + * + * @param teamId The [TeamId] to check. + * @param status List of expected [RunManagerStatus]. + */ + private fun checkTeamStatus(teamId: TeamId, vararg status: RunManagerStatus) { + val s = this.statusMap[teamId] ?: throw IllegalTeamIdException(teamId) + if (s !in status) throw IllegalRunStateException(this.status) + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 995a70e5c..437d7027d 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -13,13 +13,13 @@ import dev.dres.data.model.competition.options.Option import dev.dres.data.model.competition.options.SimpleOption import dev.dres.data.model.competition.options.SimpleOptionParameters import dev.dres.data.model.run.* -import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent +import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.* @@ -162,7 +162,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } override fun start(context: RunActionContext) = this.stateLock.write { - check(this.status == RunManagerStatus.CREATED) { "SynchronizedRunManager is in status ${this.status} and cannot be started." } + checkStatus(RunManagerStatus.CREATED) checkContext(context) /* Start the run. */ @@ -181,7 +181,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } override fun end(context: RunActionContext) = this.stateLock.write { - check(this.status != RunManagerStatus.TERMINATED) { "SynchronizedRunManager is in status ${this.status} and cannot be terminated." } + checkStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE) checkContext(context) /* End the run. */ @@ -200,7 +200,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } override fun currentTaskDescription(context: RunActionContext): TaskDescription = this.stateLock.write { - check(this.status != RunManagerStatus.TERMINATED) { "SynchronizedRunManager is in status ${this.status} and cannot be terminated." } + checkStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE, RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED) this.currentTaskDescription } @@ -208,7 +208,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio checkContext(context) val newIndex = this.description.tasks.indexOf(this.currentTaskDescription) - 1 return try { - this.goToTask(newIndex) + this.goTo(context, newIndex) true } catch (e: IndexOutOfBoundsException) { false @@ -219,7 +219,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio checkContext(context) val newIndex = this.description.tasks.indexOf(this.currentTaskDescription) + 1 return try { - this.goToTask(newIndex) + this.goTo(context, newIndex) true } catch (e: IndexOutOfBoundsException) { false @@ -227,12 +227,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } override fun goTo(context: RunActionContext, index: Int) { - checkContext(context) - goToTask(index) - } - - private fun goToTask(index: Int) = this.stateLock.write { - check(this.status == RunManagerStatus.ACTIVE || this.status == RunManagerStatus.TASK_ENDED) { "SynchronizedRunManager is in status ${this.status}. Tasks can therefore not be changed." } + checkStatus(RunManagerStatus.ACTIVE, RunManagerStatus.TASK_ENDED) if (index >= 0 && index < this.description.tasks.size) { /* Update active task. */ @@ -254,7 +249,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } override fun startTask(context: RunActionContext) = this.stateLock.write { - check(this.status == RunManagerStatus.ACTIVE || this.status == RunManagerStatus.TASK_ENDED) { "SynchronizedRunManager is in status ${this.status}. Tasks can therefore not be started." } + checkStatus(RunManagerStatus.ACTIVE, RunManagerStatus.TASK_ENDED) checkContext(context) /* Create and prepare pipeline for submission. */ @@ -277,10 +272,8 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } override fun abortTask(context: RunActionContext) = this.stateLock.write { + checkStatus(RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK) checkContext(context) - if (!(this.status == RunManagerStatus.PREPARING_TASK || this.status == RunManagerStatus.RUNNING_TASK)) { - throw IllegalStateException("SynchronizedRunManager is in status ${this.status}. Tasks can therefore not be aborted.") - } /* End TaskRun and persist. */ this.currentTask(context)?.end() @@ -348,8 +341,8 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ override fun adjustDuration(context: RunActionContext, s: Int): Long = this.stateLock.read { + checkStatus(RunManagerStatus.RUNNING_TASK) checkContext(context) - check(this.status == RunManagerStatus.RUNNING_TASK) { "SynchronizedRunManager is in status ${this.status}. Duration of task can therefore not be adjusted." } val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") val newDuration = currentTaskRun.duration + s @@ -387,8 +380,9 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio * @param viewerId The ID of the viewer's WebSocket session. */ override fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean = this.stateLock.read { + checkStatus(RunManagerStatus.PREPARING_TASK) checkContext(context) - check(this.status == RunManagerStatus.PREPARING_TASK) { } + return try { val viewer = this.readyLatch.state().keys.find { it.sessionId == viewerId } if (viewer != null) { @@ -434,7 +428,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio * @param sub [Submission] that should be registered. */ override fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus = this.stateLock.read { - check(this.status == RunManagerStatus.RUNNING_TASK) { "Run manager is in status ${this.status} and can currently not accept submissions." } + checkStatus(RunManagerStatus.RUNNING_TASK) /* Register submission. */ val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") @@ -620,4 +614,13 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio this.adjustDuration(context, prolongBy) } } + + /** + * Checks if the [InteractiveSynchronousRunManager] is in one of the given [RunManagerStatus] and throws an exception, if not. + * + * @param status List of expected [RunManagerStatus]. + */ + private fun checkStatus(vararg status: RunManagerStatus) { + if (this.status !in status) throw IllegalRunStateException(this.status) + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index f181eeee9..15fd597df 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -97,5 +97,4 @@ interface RunManager : Runnable { * @return True if [ClientMessage] was processed, false otherwise */ fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalRunStateException.kt b/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalRunStateException.kt new file mode 100644 index 000000000..86e6e39d0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalRunStateException.kt @@ -0,0 +1,13 @@ +package dev.dres.run.exceptions + +import dev.dres.run.RunManager +import dev.dres.run.RunManagerStatus + +/** + * An [IllegalStateException] that gets thrown whenever a [RunManager] is not in the right [RunManagerStatus] to execute a command. + * Errors like this are usually linked to bad user input. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class IllegalRunStateException(status: RunManagerStatus) : IllegalStateException("Could not execute request because run manager is in wrong state (s = $status).") diff --git a/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt b/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt new file mode 100644 index 000000000..55b4b0b8f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt @@ -0,0 +1,13 @@ +package dev.dres.run.exceptions + +import dev.dres.data.model.competition.TeamId +import dev.dres.run.RunManager + +/** + * An [IllegalStateException] that gets thrown whenever a [RunManager] or a dependent class does not know a [TeamId] it is supposed to process. + * Errors like this are usually linked to bad user input. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class IllegalTeamIdException(val teamId: TeamId) : IllegalStateException("Could not execute request because run manager does not know the given team ID $teamId.") \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index d5830780d..1742edd62 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/tasks/csv":{"get":{"summary":"Get api score run with runid tasks csv","operationId":"getApiScoreRunWithRunidTasksCsv","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Default response"}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionQueryComponentOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSimpleOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT","PROLONG_ON_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetOption"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentOption"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringOption"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterOption"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSimpleOption"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/tasks/csv":{"get":{"summary":"Get api score run with runid tasks csv","operationId":"getApiScoreRunWithRunidTasksCsv","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Default response"}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessfulSubmissionsStatus"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessfulSubmissionsStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"412":{"description":"Precondition Failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionQueryComponentOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSimpleOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT","PROLONG_ON_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetOption"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentOption"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringOption"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterOption"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSimpleOption"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"SuccessfulSubmissionsStatus":{"required":["description","status","submission"],"type":"object","properties":{"submission":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"description":{"type":"string"},"status":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file From 1eebd7a31a1e12e6a3ad80445f3cfb6e6bd6ec6f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 Mar 2021 13:29:25 +0100 Subject: [PATCH 57/95] Added indication to the exception message that exceptions thrown in the postSubmission() methods of the InteractiveAsynchronousCompetition and InteractiveSynchronousCompetition are programmer's errors. --- .../dres/data/model/run/InteractiveAsynchronousCompetition.kt | 4 ++-- .../dres/data/model/run/InteractiveSynchronousCompetition.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index c28930860..e9d52e223 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -114,8 +114,8 @@ class InteractiveAsynchronousCompetition(override var id: CompetitionId, overrid */ @Synchronized override fun addSubmission(submission: Submission) { - check(!this.isRunning) { "Task run '${this@InteractiveAsynchronousCompetition.name}.${this.position}' is currently not running." } - check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task." } + check(!this.isRunning) { "Task run '${this@InteractiveAsynchronousCompetition.name}.${this.position}' is currently not running. This is a programmer's error!" } + check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } /* Execute submission filters. */ this.filter.acceptOrThrow(submission) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt index f2071c8b9..151723e34 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt @@ -100,9 +100,9 @@ class InteractiveSynchronousCompetition(override var id: CompetitionId, override */ @Synchronized override fun addSubmission(submission: Submission) { - check(this.isRunning) { "Task run '${this@InteractiveSynchronousCompetition.name}.${this.position}' is currently not running." } + check(this.isRunning) { "Task run '${this@InteractiveSynchronousCompetition.name}.${this.position}' is currently not running. This is a programmer's error!" } check(this@InteractiveSynchronousCompetition.description.teams.any { it.uid == submission.teamId }) { - "Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousCompetition.name}." + "Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousCompetition.name}. This is a programmer's error!" } /* Execute submission filters. */ From 711f4c78a5879bb404c447e4e6b2a8c00bde3de6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 Mar 2021 13:33:08 +0100 Subject: [PATCH 58/95] Committed oas-client.json --- doc/oas-client.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/oas-client.json b/doc/oas-client.json index 3ec126810..75751d52a 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"208":{"description":"208","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessfulSubmissionsStatus"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessfulSubmissionsStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"412":{"description":"Precondition Failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"SuccessfulSubmissionsStatus":{"required":["description","status","submission"],"type":"object","properties":{"submission":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"description":{"type":"string"},"status":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file From 0f441e9e2de7847e73282b8a08c8c536300ad205 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 Mar 2021 14:14:40 +0100 Subject: [PATCH 59/95] Replace native color picker by ngx-color-picker + fixed a display issue in CompetitionBuilderTeamDialogComponent. --- frontend/package-lock.json | 38 ++++++++++++++--- frontend/package.json | 1 + ...etition-builder-team-dialog.component.html | 41 +++++++++---------- ...mpetition-builder-team-dialog.component.ts | 35 ++++++++++++++-- .../competition-builder.module.ts | 20 +++++---- 5 files changed, 95 insertions(+), 40 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index aa72df5a3..480463739 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -244,6 +244,12 @@ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "uuid": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", @@ -2741,6 +2747,14 @@ "rxjs": "6.5.4", "semver": "7.1.3", "semver-intersect": "1.4.0" + }, + "dependencies": { + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + } } }, "@tootallnate/once": { @@ -3179,9 +3193,9 @@ } }, "apexcharts": { - "version": "3.19.2", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.19.2.tgz", - "integrity": "sha512-hMFLRE2Lyx4WrN9pYfQLvBDcn+HOodZrqRwc+kucxM+hcUmI2NHY4z+GI14+VcSFmD4aKiMbS3z3Q2jiBxUrcg==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.25.0.tgz", + "integrity": "sha512-uM7OF+jLL4ba79noYcrMwMgJW8DI+Ff28CCQoGq23g25z8nGSQEoU+u12YWlECA9gBA5tbmdaQhMxjlK+M6B9Q==", "requires": { "svg.draggable.js": "^2.2.2", "svg.easing.js": "^2.0.0", @@ -7116,8 +7130,7 @@ }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "resolved": "", "dev": true }, "inquirer": { @@ -8798,6 +8811,21 @@ } } }, + "ngx-color-picker": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-11.0.0.tgz", + "integrity": "sha512-HyiFNPYLrCyYbFpLvZJaHC43RhjfDdFDij4pnvk9R46iH1scVtO6f2ibBgxRwBKKsT94KYvOH8wF8OrvztWdEw==", + "requires": { + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index c5bfc11db..2007a5a51 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -29,6 +29,7 @@ "hammerjs": "^2.0.8", "ng-apexcharts": "^1.3.0", "ng5-slider": "^1.2.4", + "ngx-color-picker": "^11.0.0", "rxjs": "~6.5.4", "tslib": "^1.10.0", "zone.js": "~0.10.2" diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html index 1fb30a921..05e4a0732 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html @@ -1,19 +1,10 @@

Add team

-

- - Name - - -

- -

- - Color - - -

+ + Name + + @@ -21,7 +12,7 @@

Add team

{{(userForId(user) | async)?.username}} cancel - +
@@ -30,15 +21,21 @@

Add team

+

- - - - - - - {{form.get('logoId').value}} - + +

diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index 221a3d7da..1e0bf0905 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -5,6 +5,7 @@ import {RestTeam, UserDetails, UserService} from '../../../../../openapi'; import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; import {map, shareReplay} from 'rxjs/operators'; import {Observable} from 'rxjs'; +import {AppConfig} from '../../../app.config'; @Component({ @@ -16,10 +17,16 @@ export class CompetitionBuilderTeamDialogComponent { form: FormGroup; logoName = ''; availableUsers: Observable; + colorPalette = [ + '#FF0000', + '#00FF00', + '#0000FF' + ]; - constructor(public dialogRef: MatDialogRef, - public userService: UserService, - @Inject(MAT_DIALOG_DATA) public team?: RestTeam) { + constructor(private dialogRef: MatDialogRef, + private userService: UserService, + private config: AppConfig, + @Inject(MAT_DIALOG_DATA) private team?: RestTeam) { this.form = new FormGroup({ name: new FormControl(team?.name, [Validators.required, Validators.minLength(3)]), @@ -53,7 +60,7 @@ export class CompetitionBuilderTeamDialogComponent { /** * Adds the selected user to the list of users. * - * @param event + * @param event @{MatAutocompleteSelectedEvent} */ public addUser(event: MatAutocompleteSelectedEvent): void { this.form.get('users').value.push(event.option.value.id); @@ -72,6 +79,15 @@ export class CompetitionBuilderTeamDialogComponent { } } + /** + * Called by the color picker when the selected color changes. + * + * @param color New color value (hex RGB). + */ + public onColorChange(color: string) { + this.form.get('color').setValue(color); + } + /** * Returns the user for the given user id or null * @@ -83,6 +99,17 @@ export class CompetitionBuilderTeamDialogComponent { ); } + /** + * Generates a URL for the logo of the team. + */ + public teamLogo(): string { + if (this.form.get('logoData').value != null) { + return this.form.get('logoData').value; + } else { + return this.config.resolveApiUrl(`/competition/logo/${this.form.get('logoId').value}`); + } + } + /** * Saves all changs in the dialog and closes it. */ diff --git a/frontend/src/app/competition/competition-builder/competition-builder.module.ts b/frontend/src/app/competition/competition-builder/competition-builder.module.ts index 23606f211..f3a12893f 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.module.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.module.ts @@ -21,7 +21,7 @@ import {MatChipsModule} from '@angular/material/chips'; import {CompetitionBuilderTaskTypeDialogComponent} from './competition-builder-task-type-dialog/competition-builder-task-type-dialog.component'; import {MatCheckboxModule} from '@angular/material/checkbox'; import {MatCardModule} from '@angular/material/card'; -import { VideoPlayerSegmentBuilderComponent } from './competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component'; +import {VideoPlayerSegmentBuilderComponent} from './competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component'; import {MatProgressBarModule} from '@angular/material/progress-bar'; import {FlexModule} from '@angular/flex-layout'; import {MatSliderModule} from '@angular/material/slider'; @@ -32,7 +32,8 @@ import {MatButtonToggleModule} from '@angular/material/button-toggle'; import {MatGridListModule} from '@angular/material/grid-list'; import {AdvancedBuilderDialogComponent} from './competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component'; import {SharedModule} from '../../shared/shared.module'; -import { VideoPlayerSegmentBuilderDialogComponent } from './competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component'; +import {VideoPlayerSegmentBuilderDialogComponent} from './competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component'; +import {ColorPickerModule} from 'ngx-color-picker'; @NgModule({ imports: [ @@ -43,9 +44,6 @@ import { VideoPlayerSegmentBuilderDialogComponent } from './competition-builder- MatDialogModule, MatFormFieldModule, MatInputModule, - FormsModule, - ReactiveFormsModule, - CommonModule, MatListModule, MatProgressSpinnerModule, MatMenuModule, @@ -55,14 +53,18 @@ import { VideoPlayerSegmentBuilderDialogComponent } from './competition-builder- MatCheckboxModule, MatCardModule, MatProgressBarModule, - FlexModule, MatSliderModule, - ServicesModule, MatSlideToggleModule, - Ng5SliderModule, MatButtonToggleModule, MatGridListModule, - SharedModule + Ng5SliderModule, + ServicesModule, + FlexModule, + FormsModule, + ReactiveFormsModule, + CommonModule, + SharedModule, + ColorPickerModule ], exports: [CompetitionBuilderComponent], declarations: [ From 9b857ca5f00d5f550bad07b3e483e2c08263608f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 Mar 2021 16:27:23 +0100 Subject: [PATCH 60/95] Added the ability to change the order of tasks. Addresses #220 (w/o drag & drop). --- .../competition-builder.component.html | 11 ++++++- .../competition-builder.component.ts | 31 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder.component.html index 3dbdb271e..f3c10f186 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.html @@ -192,7 +192,8 @@

Tasks

Action - + + + + diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index f0fb7de5d..cc7e06eae 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -245,7 +245,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat * * @param taskType The {@link TaskType} to remove. */ - removeTaskType(taskType: TaskType) { + public removeTaskType(taskType: TaskType) { this.competition.taskTypes.splice(this.competition.taskTypes.indexOf(taskType), 1); this.competition.taskGroups.filter(t => t.type === taskType.name).forEach(g => { this.removeTaskGroup(g); @@ -335,6 +335,35 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat } } + /** + * Moves the selected task up in the list, thus changing the order of tasks. + * + * @param task The {@link RestTaskDescription} that should be moved. + */ + public moveTaskUp(task: RestTaskDescription) { + const oldIndex = this.competition.tasks.indexOf(task); + if (oldIndex > 0) { + const buffer = this.competition.tasks[oldIndex - 1]; + this.competition.tasks[oldIndex - 1] = task; + this.competition.tasks[oldIndex] = buffer; + this.taskTable.renderRows() + } + } + + /** + * Moves the selected task down in the list, thus changing the order of tasks. + * + * @param task The {@link RestTaskDescription} that should be moved. + */ + public moveTaskDown(task: RestTaskDescription) { + const oldIndex = this.competition.tasks.indexOf(task); + if (oldIndex < this.competition.tasks.length - 1) { + const buffer = this.competition.tasks[oldIndex + 1]; + this.competition.tasks[oldIndex + 1] = task; + this.competition.tasks[oldIndex] = buffer; + this.taskTable.renderRows() + } + } /** From 3ba067348c4cdb8ee5a430ca4c53b382929b599e Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 Mar 2021 16:27:53 +0100 Subject: [PATCH 61/95] Added missing semicolons. --- .../competition-builder/competition-builder.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index cc7e06eae..37eae29c4 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -346,7 +346,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat const buffer = this.competition.tasks[oldIndex - 1]; this.competition.tasks[oldIndex - 1] = task; this.competition.tasks[oldIndex] = buffer; - this.taskTable.renderRows() + this.taskTable.renderRows(); } } @@ -361,7 +361,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat const buffer = this.competition.tasks[oldIndex + 1]; this.competition.tasks[oldIndex + 1] = task; this.competition.tasks[oldIndex] = buffer; - this.taskTable.renderRows() + this.taskTable.renderRows(); } } From e8722a12cc1a83228a9502e1bddd8ae5748b2ffa Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 8 Mar 2021 18:07:51 +0100 Subject: [PATCH 62/95] Added execution command to batch-execute DRES CLI commands --- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 19 ++++-- .../dev/dres/api/cli/ExecutionCommand.kt | 66 +++++++++++++++++++ .../kotlin/dev/dres/api/cli/UserCommand.kt | 1 + .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 40 +++++++---- 4 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 7429b02d2..78c1b6da3 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -1,6 +1,7 @@ package dev.dres.api.cli +import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.context import com.github.ajalt.clikt.core.subcommands @@ -23,13 +24,14 @@ object Cli { private const val PROMPT = "DRES> " + private lateinit var clikt:CliktCommand /** * blocking call */ fun loop(dataAccessLayer: DataAccessLayer, config: Config) { - val clikt = DRESBaseCommand().subcommands( + clikt = DRESBaseCommand().subcommands( CompetitionCommand(dataAccessLayer.competitions, dataAccessLayer.collections, config), UserCommand(), MediaCollectionCommand( @@ -40,7 +42,8 @@ object Cli { dataAccessLayer.mediaSegments ), CompetitionRunCommand(dataAccessLayer.runs), - OpenApiCommand() + OpenApiCommand(), + ExecutionCommand() ) var terminal: Terminal? = null @@ -99,7 +102,7 @@ object Cli { } try { - clikt.parse(splitLine(line)) + execute(line) } catch (e: Exception) { when (e) { @@ -117,10 +120,18 @@ object Cli { } + fun execute(line: String){ + if(!::clikt.isInitialized){ + error("CLI not initialised. Aborting...") // Technically, this should never ever happen + } + clikt.parse(splitLine(line)) + } + val lineSplitRegex: Pattern = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'") //based on https://stackoverflow.com/questions/366202/regex-for-splitting-a-string-using-space-when-not-surrounded-by-single-or-double/366532 - private fun splitLine(line: String?): List { + @JvmStatic + fun splitLine(line: String?): List { if (line == null || line.isEmpty()) { return emptyList() } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt new file mode 100644 index 000000000..25ea5d807 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt @@ -0,0 +1,66 @@ +package dev.dres.api.cli + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.arguments.argument +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.types.file +import com.jakewharton.picnic.table + +class ExecutionCommand : + CliktCommand(name = "exec", help = "Executes a set of DRES CLI commands in a file") { + + + val inFile by argument( + "script", + help = "The text file with line separated DRES CLI commands" + ).file(canBeDir = false) + + val quiet: Boolean by option("-q", "--quiet").flag("--verbose", "-v", default = false) + + override fun run() { + val lines = inFile.readLines() + val results = mutableListOf() + val commands = mutableListOf() + lines.forEach { line -> + try { + if (line.isNotBlank() && !line.startsWith("#")) { + Cli.execute(line) + results.add("Success") + commands.add(line) + println() + } + } catch (e: Exception) { + when (e) { + is com.github.ajalt.clikt.core.NoSuchSubcommand -> results.add("Unknown Command") + is com.github.ajalt.clikt.core.MissingParameter -> results.add("Missing Parameter: ${e.localizedMessage}") + is com.github.ajalt.clikt.core.NoSuchOption -> results.add("No Such Option: ${e.localizedMessage}") + else -> results.add("Exception: ${e.printStackTrace()}") + } + + } + } + if (!quiet) { + println("Successfully executed ${results.count { it == "Success" }} / ${commands.size} commands") + + println( + table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("Command", "Result") + } + body { + commands.forEachIndexed { idx, command -> + row(command, results[idx]) + } + } + } + ) + } + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index cff7a0035..9d8653f02 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -39,6 +39,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { return mapOf( "ls" to listOf("list"), "remove" to listOf("delete"), + "rm" to listOf("delete"), "drop" to listOf("delete"), "add" to listOf("create") ) diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index 871d93407..2e8d1698b 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -29,34 +29,46 @@ object UserManager { fun create(username: UserName, password: HashedPassword, role: Role): Boolean { validateInitalised() - if(username.length < MIN_LENGTH_USERNAME){ - throw RuntimeException("Username is less than $MIN_LENGTH_USERNAME characters") - } + validateUsernameLengthOrEscalate(username) + validateUsernameUniqueOrEscalate(username) val newUser = User(username = username, password = password, role = role) - for (existingUser in this.users) { - if (existingUser in this.users) { - return false - } - } - this.users.append(newUser) - return true + return create(newUser) } fun create(username: UserName, password: PlainPassword, role: Role): Boolean { validateInitalised() + validateUsernameLengthOrEscalate(username) + validateUsernameUniqueOrEscalate(username) + validatePasswordLengthOrEscalate(password) + val newUser = User(username = username, password = password.hash(), role = role) + return create(newUser) + } + + private fun validateUsernameLengthOrEscalate(username: UserName){ + if(username.length < MIN_LENGTH_USERNAME){ + throw RuntimeException("Username is less than $MIN_LENGTH_USERNAME characters") + } + } + + private fun validatePasswordLengthOrEscalate(password: PlainPassword){ if(password.length < MIN_LENGTH_PASSWORD){ throw RuntimeException("Password is less than $MIN_LENGTH_PASSWORD characters") } - if(username.length < MIN_LENGTH_USERNAME){ - throw RuntimeException("Username is less than $MIN_LENGTH_USERNAME characters") + } + + private fun validateUsernameUniqueOrEscalate(username: UserName){ + if(username in users.map ( User::username )){ + throw RuntimeException("Username is already taken: $username") } - val newUser = User(username = username, password = password.hash(), role = role) + } + + private fun create(user:User): Boolean{ for (existingUser in this.users) { if (existingUser in users) { return false } } - users.append(newUser) + users.append(user) return true } From a81846c0cef7a8a4b7c3e85cbcc08f20155ca34b Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 8 Mar 2021 18:23:21 +0100 Subject: [PATCH 63/95] Added kdoc for usage of exec command --- .../src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt index 25ea5d807..39350295d 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/ExecutionCommand.kt @@ -7,6 +7,13 @@ import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.file import com.jakewharton.picnic.table +/** + * Simple batch execution command. + * Supports text files with line-separated DRES CLI commands. + * Pound (`#`) prefixed lines are ignored, i.e. treated as comments. + * + * For convention purposes, the file suffix `ds` could be used, since it's a `dres script`. + */ class ExecutionCommand : CliktCommand(name = "exec", help = "Executes a set of DRES CLI commands in a file") { From 26f85e7acf6e4667202a23df7a9b0552b94bb089 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 8 Mar 2021 18:23:58 +0100 Subject: [PATCH 64/95] Regenerated frontend bindings --- frontend/package-lock.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 480463739..9eb52c196 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -7130,7 +7130,8 @@ }, "ini": { "version": "1.3.5", - "resolved": "", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, "inquirer": { From d8596ee9bd0ea1c8273a04ab36ffdbaf142396ab Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 8 Mar 2021 20:18:26 +0100 Subject: [PATCH 65/95] Added color pallette to team dialog and changed display of color selector slightly --- .../competition-builder-team-dialog.component.html | 10 ++++++---- .../competition-builder-team-dialog.component.ts | 8 +++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html index 05e4a0732..f74733739 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html @@ -24,10 +24,12 @@

Add team

$|c zhPVR@j{Nk&@@WnWOc#pdglpYbnqRa^G=aT%gEf}qea-{5y=4?s%BNYdE!7oVS$dS-jq9^$`zdUjKX!{5-+8{58s{P6Lviq5&f zzQ9h(E0*q;!DLs~tXi-I!aV7pA;bDB7h>Q7WQv&S-Bq<`n10M5tTIoGCSRX*XE}4X-yKG@ z*&4QX9KS>Qd^uW=HyX#d?b+Z|_#4p&jaPATz^QW2$6y|u52x5h~<_a*4PTBwZbROhRfpidY0V55XOH=QZt*L z?wXbvb}pSCneNmv;npvM38FTjLG&fWle~TLgc1+1AD2J@-r>rMbWHR(3+UZ0jQw8 z5ZgwgvjLFKKD^9oT<5j>3@y0WOxmAzkAX#?$$^3yw3s8prBpU|GV=W*PJEt$__E@r z`Stz2}qfA&J!S5F1Wi7fUqy*vAkIja!>mdAZeJkLWFEIi5V(C}TMXN&@V zj#{qpaJwA^>(sLa%xI%rHzgljPs6aV0K4sn;+ABIp zuQB63*`WGhyF+hsmojX3a<7Z|dh7vbcsGv!>0JUB#7*nn5*_9p6AkHI6Wmdy%>ep) z92}15`S_M@$U7q1>&W2ode_xEfne`?Ttb+ss&eG-$>$fH5bzVZdcs(H6oyFkfkhJ2 zUwY62^V&sX)S&ZfJmNGw;q5^Mk~pP+I3uP&`9a3N8m?f>3PXU5SD2nu=9@r>IfA+J zjjX@)X!vzOM-bidc2qIeh#Q>abJC@x<${icn`KprM)xE;qpkSS z|JucpB7kj2oOa8B-r7Ruh=d?*aqcTYdc@#rq>#Q8xE5LNkn$PR`-Y6+D$~(X5QkUK z3)eq)aPjpu>WI=wtB;d7loeZdCF`1Is64W#Ofa`qEBek>9wxW(>0(*7!kB?7Gwit4 z(Gg=8?0SZc;x|X>@MS+o^cebx=8&<8x2{&j1suO|vFF`5buf&7R|&Qg33jOwySf?< zazki_QODaxRqIi;T}qOw`^~gsa_&6m>$vu7N(aezO#KJ>Q~BF13Rr) z0$7K!w7&oXenjB`WX?|*Va$7e`O34 zLtF`Fb~J02*(;X*%5<(50Y(ZST0aGf0h!-(n8&AzL*F`sMjW4*!1BEp?hbH}9S$7f zO%LbIz}qGL&=^by4)9Dd=%7S6^z|#^5wHVEB6bNV8dB3(#{y+03YSOj$ z1(#|0&|-U;@#j?3YNOn0BbCI6-El=66+Lt{i8c(al1Q8EO3eS}T^m!rrCJVdp@Zzq zAiL(E_PK2Ojap$H%gcM8Dvq220xmznRkY$W41^Al2d-gwOQ{Sffi;Tw#nfQ z!8Ky6Fnp@zRJs|43%>a^AMi-vw|-SlWCSf+Wrc2Sko%DI7J60saN5_Sw>^miD=&7& zN~fxsIWvtH-=lfmgMF6IcJ9%DsG#x2`>pAVb`On1v26P|ynUXy#+;Z5sh_4inhVcjhBn8vLv@?^c7~MeNp3Oc8s!_1aYTuu4%I2R7#UhE zeC1~&Ugb+8uBy5n-UPduT4!hS-A&I-y_EFkn`P7d>6W-+E(UwpwHWW~5!9Fl%rhU8 z4rN-lV^xZE9tstUT+j$FGG_R`{nZSVEdIRh#XZ{{CF~w9=z~HC3Za?3cVIqLV7t-KNDDA(PuVk1EQP z<;!`;CMg(tjBc_oR_9o}$LsR%*(lK3;qJ$NpY> zz2Q%SK2J9HPdVIxM|@u?$Ai{=N3RQx8WS(WUmi`K5tJ9V6@4gz0rWS$C?Nt0Z0;Dp zN_9*dEh%Lz9X_yqMWoXnNtH!zgtATSXNv=2l;<=fNed&L!s?27>;+%8%xsZAJYAB> z6*7-ODl4v5g{=50{n>@`0v& zL!{*eD0MShOB38PGu}00NWP}zz23BV?b-8LU7Sutt0p2PG4|hMlDSr~Ovdo_g{v;R ziPuB~LwSn8jpVxkpS+hQSqS-OwkT{yq&tp9>Jy1O0r@%+1*(LwL13yrR6IKYRG!dJ z5t>vF=!WguGx>zusRaBza|PX_wgeTrm#&$9@d6;7K*6 z3e6;^pCyeEsw#~Bbf^@APG9i1S&_$v!%F-4drg!~g!lGP{ z?>(t@{R9);;EhaY*k_PBL`Z*fk|?BYWDC)YXsX7k=SOzbM2c@%WY{Tj&s*u?F0^dq z3*p4AojqtLL^ifH$GHBRj&%G|gRw}kkvKT^mqiZ^JjXwysjH#3$qGU*++|#Yukw`C z902-@ySHLM;e0%y&tyK0oqj2Qj>Fl+q~(%^o>dC+21Pf1y= z9SBh)|AdSSszzwvjk$C@;k}$FRI3&YX1W~XYVK8jRjUo+ooRFNVsds}z|@6ejWB&H zo{iQiz*G~Az7=mMsx-NwiJzMk=)7XcHKvoRulV<0mw!j(wZ}Np{EvkTM*{&N`#)LVofk!b)(#1unSe7mChznn;^*XGaL302Sf!K%NvK1Sp(SkQr9Q5)QXLWZWWSAO z=$Q}C%616O&ya9od*vm$4d-U_o}94_2TOV^deIt8leMP35r4xTw#k2VqZrON?~xqG zd80SRHXo|9Lp+81ZSG7&FRaS8pQn)X}sTvE9 z8-2y&E;K{W@gb+OsLCsHxpuL%Q*$;yjJFmUTh_VCGv#Nao;^Fzmo4P6+GfOj1srPR zsg<`)HXo#SG|j@XaGO@mRa?jd4EM7ECE3qIQ%*s#tLnCE-zC@}S*5I^><4LN)Jzvs z7(Ovy+fmt|)3Vmq99k((g!1juobDEhgZk`E82CJ8+jRbGN@Cmc)iyuK&pOT6-u^~0 z?zVb&Q{|S|$`FExDI}z6!__sPe1qpFTF6w-Wr~OJY*`zQKHKz^TI+JLzcOfmL{)Kl zJ5Oiy@1!;)n?)1Y0(T3g27L-^-p%(?3VBboNm_z;Qv__0l2J9;Z7)Y*yw&7QoN3rq zqBZ*j|NbhQCiNUnQ@nIM@-fgMU0K1>HcT@Gms;}(PjPmb1ot;MCB9qyB&1@>L5;9_ z{`8ryhVDpDwIcp@l)FzNsW>FSt6C;QVyJ>5H~Ah!hu^|icE~1Z+HKCAyxZ-*5zAut z$@jUliq9rz#UW27(F(*uio;<$`%+wYGOD&dRM0gct-U1syj0&lJ2Uo2%Wf>5W_007 z6|b14{7?m^KqM>Vwo6K|!bYtzJoo%?99+9;POxSx?M4v74^6m#O(70!z!t{^NnSa{CzL$VB8p^=*hcrsN=Y%vG=Y`xK z;HDHPKG5@4AM9YIJ>-Y$kGX?|$WE@lrFjzy{2_S?@}s*(=Mb6lQ+hBV>zewlDzt$1 zjW@99Kp?Q{K+9Wx@c0dA3*K-1-X~Mcv{^=&HSruG_StDpNGhbh=ZF2Jbr1ciGMMs~ z5-fboEUpih8Ct1H?=VuWFkPjX({Vj%D_dwgUaZIg8`{muX_3W9@u$K6;Md_DT>en$ zz>>|KM}>lvlC_%Xrj~nX-p$Er3nje`^H}UaT1&9x!H1muqW%KVQWi7iX~L(xm}O|~ z#X+Z^p7)e&r;?wMZm3UN#ARz&eSB6w5T2aYZXTDbQsB?2PjNoZX_YZm@$&ei*e4@< zA-cu+gUfi6DlKU&8_mLNOcm>F1jm)ZN#O zzmg?7)MT_mL2tU;A-l)smN&w_S2{1pQ=5j1v$uP~ENtMAB$VcrcK=oTxtT$B+CQ@1 z&$A^a(@Ka)^h5oyrh0OJ9#gzHDSSme!!y-3XWzXi7nHjZG|r9HxX}HbNrSM@H~AmY zl;6^9=(;b^Zt+UFk(}@*N9)52iLX64_U|w5ZFDiXM=}GaBCMv6LFw6*L_c{ieT5wU zA*K3qh9dlg5ClEl8!d$LFUsxZkcrz%g_DI7%&k#4`w>l747u8#01iJHoi4xrit81G*c; zYb5(ZWKaogi(If@57k)E;!!8`@909<{p z=s$mO)#Hn^uppRy@nfi*eXNEduoB3`4OXj`wMD-$aYLg5Z3?JcABCj9(eM9qE$;{i zSr|+MQmg_su+tUt)(;V1Ix41MT9z{O4P?P%2=!FO2-SVFcG~_M0Mz^g>CJ1Y!FEQA zHyFL{&h9x}E5uL`d#9o?MvjkrU*Wk$C8c(0X?SXAh`aoJc5Sh92J=H8jlQpnncsm)Idi-{nvpE5rUGUtD3?&^ zQA!Xp6!|>dyvmL-u7;{H{HUpYJ};Lq#^Xz%+mwJ?UNka*xdxQeq!<&L7B=xyjxRPN33hW zC1xWWsfp0wh{Q7n;w8D<(WFFbCqeLtz;~2_rAf<{}UZ{%l{`lUH=;0FogcB@M+7 zsxE?(;|m`8I}S!ggq%wlmQTGYZHei386lY?Ua%`q$e#*X%Z4LVb0rqa@Ga5|!*P=i z;_$=Yl*Yv=l4-5&pnFE#buT}@iT`i@WCJ0Y!XNCv9~b{YU7-Ji;v+M`05Xnl?k4v3 zV%8RBcK-vTq@@}tp^IRI@7r`3bnl8X29gx}%jwbS!DXY2;>g5ONief0+&gNAH#dGw zIM#fVJ9RFI7cY*;F@LIzvA4+S$s%$n%+GA*z4G2|X6*_Cz$cjU5IMNZiG{YJGR?&O zk8*mxXjgsC#2+%_ctD8CpSON`LoVB3lUDzceYa^FZDs;3fpU209hdF=4Xpn8npQIO zT4$d=+uK%w3d1rD-_Gbke~nkY9ghyAuz=d7?)!HA-+za!Hf9Xf&!-R@Y$2&?k%^qR z!mPql!wm6O7u)gvs+-r|tc+fJIw*NNz312HbK3vb>^z?k=mjd*=j5*gx7%q=HYW1# zoHMT;s1EH>@Hp-R^Ky`57IFot`W=QU^7t z;cG_8#7O-tI?W}7uzG$bL)asLUI^n^6>`FE5#YIhLr~6dsRnqkSuAbXvo%X4J)3@#~v?(nVQ@fLH9eXs5+tESnmjDP!C+z7& zgB%)&-kK9JNw*s^M)Svlxj2@T5hHGyzeuRqzmV-4y+LEZM;2?M?z}vE>A&MAERIsq zm_4|9w3ud?(4qh4YOqGMZUuapqlR)_{r~Rmi6@wI2?huV6C(%+$^RN?=>LzK(t-6? zUtJZZZs|4gW{3)9u}6|7p*N8NGfhFEzyYIVKwPSfqE*wyvyV)q1W1qPNW{5$W@nxyc7dHeeo_II!6b;oV~QTROH z?>ypP*BuTjgn=%_#dr<7zqu_bNG;nr@%(;=gz%uHG^@hJC9(Y8zsRW2Mdqhmh&y(MjXqK7r9NtDPnW7M)|6ga zFXNK8OX3g#+#{MHUMZg;Rl#(b4(aUFgL~{e(mh>VjfJ;IBCt1YSNfC-(vWuEB@zg| z4$-M8c_segA*xMWN@hrW@O088avV-lcM^wJKwPS!jg%L+WS?9v^Jbr3r3dz@c)3~a z(lWLU)+;!`UutHRX!~xk)CzIwvsd=u7`M;ZfDLi!AE=bb^%#a{gi$&>6hMv->X??m zWus}kLsWWe57_RYo+$oKra~*tTXJ(r^mL;c@H@dxv$RMwBS3a0o7nZAj8U+-Qk6^n2LK4ApdP$ z+_K!7X~{uVJ6EkZZJm&!7OzA28Ljb&yCQdq;A^e z?`VDC`}TBGQeq6cLnieythuwOI3Dd$M}#X+kSHK?}`= zO2TQqq_tFkr;cKIbix;x<@&AuHc*PNE{a)$vE)0$*F5HP0dP^Jdd`wdg%`T;q-y1O~&P9JR`&o<3>izFdsLFd_A^gxhlP9 zfMw(q%D9Geb&<4jX4_!;~84j7z`uPWvp#QwOuRSact(~ zHxk^QLiml7pvftjiW`h{jTnW4!0ll^@a$m~OeJ7@l$~VSU{Lok9>fEvUa%_X3ss?0O>_?HpgyOLDgdTWC@F|kX z{Nyh7W(%uos@?Fml*tO=AWr%{xSW)`x=b&iMd|WYw~(qw^b2c{9t`LMLS`L$`S&2d z@xJhw6h~hr3m>xTUDB^X3vfH?`fJfsgIL?(bZQMHDe4BnHwj1wlTDfd-U=hOlE!G5 z=4jW<)p0de_M9z2Sq7&b+P^XF+$nT1WA(p(BnuZkP=l_vi+dR6Vj?7}Mf#uZY~_Vb^qYp}i*VUR0DkWWVx+yKgMf`lZ*iUmj5*w1;n&;o_y=bEBfxf|UCQ zN`Q7nAl||yUOy6if#xb*O8xpJUAJxpy-mRw_r)jeUc*zxyJQ?7Oim2#-XG<(_TJ*p zZadNOQiBh9!HE&9U0oLrYu24nLj^@!Qc4k<1?(H)9-lx3CR!%dYhz>CCh{ zXX~&_^v-+UiSiPhf6amWLoY*8W03WEqRCMVHFZ!2%}Ko*#@#LZB>oz0eK^63xkix7 zwDdsknE^Zh`<|&-P_gY1_>*|$eVc{W>o&2Oda2yik7p4$s(_qHr$SSolZuwgWlAGg*CouZ_Z(2OY%Jrtj<9IV?M3OuQ{iIf|~x z+OCWNe~txonygr@9v+#y2&5>OFY2d4Je)llU2N>-U1%Y2eMnxIhQRK&)ncRb` zgHl%)N*40F(cQOsjH1!_2+(IRufIn`ayt8{pN|7Lhkrvs0;~Nj#xYzsd-GWHfDt9Q zl8Vyz2bi+vw1PRM7>Qm}rKsit^9e~!XTE>x6PCX`P%9qh-2=mc`lps;{+O||%vRW` zE3_)>o@j#9uE;_8JpA88Ozy*kur+)P@8{Tf!WBvd0`Z!Xf6%X)yzb2S%KAYzn z?rQ+Pf5|}DAyI|U%KQ2!ejYIWOHAfU7xnVl>bq|aK#}@R`j^^tpV_Q%zE1JyOJ_&G ztreXsq4K=^wES>~wCSJG2qCa~N&Kvo_ixMy3{ENcGYc!%lrXfJmz`lSHrZwJl6Z_= zNUMIzH^pX5kKm`9OR}Egne*0~1*LE9@jft;w2EN+%=*AOq{7?Aw2aZAs-6c@dc$O6 zomi1GxZ^0H4Ca;nB7IApks9BRmBkfJb6#vB8+E-Z`eyEF)qU??AGEiYCp$>fwCY1s zvv&_EhvL3@okqXsS;VzuTTz?q2YJc8MBR$F-jY59>8bS-6`H%J8fgf&Q&Rt#=M(_L zAV7vpgL>ey?%X*2D14?^=Qk{@HMnovL&kHwCQ-v>A(8p8F)8y%R0?MD1 zBo;$)ZMMs$Soe+8MqZh+jAPj|t_y5veA>_UYumoK>5uaLdCKF)-GsmPJ(y(0n*P{i zys<-v#V0j(cM>ICv&TEdeBw$~Ls1r0G?i3-TnPp`B$ zRxBck=XpH(i7~L^4z1umh!eEgtRz|5MoouBMTeJa>yFHNtlZxwu9=D3uMln^sN0{J zKNjaga6V#rWdA)4t@RqaSN=$s^94t1i@Tz;o3$bJnF-4*olwZy9?s21?8%lzKwpZY zc@lKhbN1Dm&eJGYR;}1jg9BD(H1sY)*D+lGBwrBd$h{P0$Ma@6$#ws<$_8ZR>gSwY zJ*_vjPnewj++L(AbAwjtayY4c6W0(YfhR?bW5EmauaUURIxO?jIbEa=xA)5 zrlxc7qeYUm^|nc4@6e1Ao$yykN@x*-8@_p}bUmn)(DCP?ZdBqM!9$!YS;}i#&RQRw z(jW5~!{0^E-Q9$IB+~P7snMnyzdv!?;G~ZzTHH=cwC`$t5MTFWEV&ukiLw=bpbLHs z;w~&?-o?0Q9A!c%smUpNDiYcO!w?lgLYc-F*c$* zp`>mI6a?gQ6@$1t3Vr=8xxF>Le$ve~0~R{P9M>zk>p6Fii_@6eL)P^|pLMZ*cq8ZT zRs@N6m7Nr?AjnqVjL8qbh)4=&>70@lIE_JpCQ5I-HSbWKD9&qFZB*W(g|vq}7n1SG zxt2G^ExxnzcFez|*w~eP;&!werZX52sJ{5wlS^m*<=#_lSk{d<3k7imnI;A!m#?eq}DTrH*>+KD?X?&h@m?QK9Bhz5`T4>^h1F zsFSSbI3%1+i}u`K76l5-HceoXiRtAR-ifN_vGYWZS~?vVHMfMj!f;OPBfh8x`-l6FcwFKm-v^o^~0!Hfa%+63{M_!aGh zVf9RWH;A5WgE-`#Z(^zBOfj9kH|dER6c@UO>GdG-%;`0~r0&heV$3h1hAh{9t7a2f zoOe&3L|XVmfI~H4+?6SmQ5wVhtT4nIHVipER(0W4w80uT9+kOMZG^lt_1(_2AFPiN zQuHmwW)(bUb?yS85C;Al>IeN`%vR0Aa_aG~72-XK3#%&2ycqhdc^?knE}sZ$gZwb_ z4Il6a-7R#8+Q`gI+hlj&D49gZC<-h6H15E%^oHW+v%IHB#+6>sc∨fd|u2@Kuk2 zqV*-j@Nbm2ZIgh~9-xQQZ~nA86An7{JNxsTptKPoK&blwV++LWv{gP zysYMJMFRZ`3aTs5<13*|ci-%Yt>~ZzWECwI8=Grr-Jqd#TI-Gm)l6@@5S*xx&Q+Tj z*llb&#HlwTK9VwRDT{mYIKFI}Q66VYJujH3eh9cv@QN`01Tekjg~Jz{b(MG4ew0e&Vovmhf&Maas#s zJ3xw(y}%24W~f+{tQT=7TGW@*JG4>$P`iGArS?GN>xyQblbe(8C@lC3Rj9oCSs5P& zqqIg!6b1E3ET99k#0SKRa^r&x8(QN)zGL>dV543>^OM+qK2G_7Z$c^uswYEU0y3_U_0z)1gLeFJ{8IW-_RpT`x>}pLXi24&E~Q zejvG+ObS{gx7NU|e9hyS_8ngTV)?}YTf8MGr1A_; zR}3^PWz11xIFL#Jm{eO$L~z{;`mrPzkfbAAQLsTT90tG{{^^)rGUMpIm|>hi*F{h$ zA6#71A~Aa%G!LQ~5bYQJF;Lfc5>HASm_iTZ!9i!ajZh+!SRNM(JAYj45ZpT#4FIr@|7_KAVJmk8q$GUYZ@m-4kAw9e2~+llyKYuVa=-*)bRDM{Npg_oSmJ zf27?tYA>Ct!dV|{1Kayddiaajlw^n=Nma?4{awL?4(=IAho0=3cPy+o0MOat?UBd^v5Sgw0rF zeV44FKMFnZbj%rG5W!w8ZH?ukP?Eo(m$r}M!P~qPjSzRl@Chhs5D$dLX*L2g#}He2 ze1epjf!-kS;CHm1<4)#qWECRfzyjsH9D3&oep@QTzTu%rY;Oi}~a&^-be)0)hO|ebt<`Ld> zitFSNYD~Vjo$|D0#vs>|cqu-r>SG-I5hHK!HsuUr+%?IiL)fg`vB^E=kY}z;N-}fA zK-F6GYwSm*5nHAR2qMMeet7qlh#1IXA?;z61vCzv%Yv$+)Kq^vGIdtr{;As}dc^z0 zIqgKaal}w$EKNU4N!w3j{L7RFc7kwu(#8z3@8d-W3fXz?+%C?hvkjMHfIBq{ys>4b zncycCgEw!A%S4jZpfWS-XrTlIEEN5N80YqNeFcqo_9g{yE!Mwg<%~Jg)%4|4_jO77qqKcaUwRz9$#*tI zLQRXc7-a|I)|28DZU~cIpA?_0pE7$G20=VM?5fG}e6}0v!TdW(ah!f{s(xH#GL<^B z9a3x64X|6<(&!eO7iJCfnO>mh%aoW7>#>NktbW&C2x{GKREG78c%2~zM<@8@Yw-gb zn%aBe&}lS318O6+z08?!54U>t^U~Fyu>$5=D$DXv9y@9D()THXBr#x|gbKD=wM{|v zk4CSMI2grVEd=eziL2wl3ltkUf3LRDE_|mjGo^dc2p{3g64s6;^-yPyfMlTjNzsq` z1vPo&pnKHO!&sQO%*q;FN480{;XNu_=CV!cP>LC;iOZbf-E{_z^N=<5 zvl7`7=^XXc9yaneb7Hs2RW^rWTCI`z1-Q)xW6jTsEM4mYT*-D61b3fr&px!Ce{)Uo z+<*FY`g#N4g#41=B)llVj;z?Lp%0c}yj0#3B`~?Tfd{c=W0}as)l3TD%X<)_Pc6}e zwe+lJ2-=0;wFC!wY4*}x$Rg#KhNuV<3>MP}#!kr$Z`Cue;a5>#_W8gNmzmmO^Y!Kt_f(45W zg^%5AHwmPwTW_Z}_NK}50I(ZXRlcV#Tg$wF?R3}Mdw(^4_4UX0Rqxk?$2GpUCNMJia5Tz5`CL}`S22h;+etQSB_`w}A7mS)esJ@#E%hsFvt|rQXtgWd zlgu9{eida|@M=bmQ8bvA+n|05$vQT=3K3C(2u>AroHpa;zR0kz?kYQyq~0{OtA(q! z)7V8+v&3U6#A%i~kZ8r|-lxvXtIow!y?nesQqF0UZhjzQS8da@sHT3}aj6~I*z5Bx zV`P;{cZLIRQApsSN;9^LY73axDXreG?y_r1ez!26mH*_1Hl^oEe3pYKGy=|48=tK_AuzjzJ?xZ44#tqgeNr0k8aZ-uevf1sWp^mM|_k z))_U;gyQh>0`yKN`X>?*j+e|JQ6}Cc6_L%O_8{0xZjf=$ zLNJ1P?R0IJ(=SlVd{^bO-uk%d?2H`TLgJ;`;ysa{lBn#+9b7f-g*;K0>7hnD<>ox- z7n{%EU1QkB@EwaE-S#c#9{W3jw;6uQ{bc5kSa?L$TkklZqn6;VzMPB(r=`zF5UpuO zDx9KerivulEWiToRG#AIM;hg%W%3FX0zN&Fk%R})%rIq9)oar0zj%O zE+LuWs(4a7Oh*SxZaeUor}lhYDQ#tYr$Ty>ex*5HvA3z{LsA(i*T!lvnQrf^*%s?I zGm=(7lv71+Wo5OuZck6DJC>Ft0&juuk0x0BHQU=HPRDl+*aXpU8w9)B<~G(qlyWu^ zE+fiet=0@$^A>72P6gK!juHy%9kkzG=DxI7B{+A*dFT1Myg;3K-*$`@`Jh!pYd){tTw87j%=(!!MJ_F>T_*Sz}@pEOstYU*deJ)TEK}x?s9F*M@+mAhG zifyCtE7cK96Tg>nDS>y&16y}#j@Gksr=1r%oz~6Uukp;JFFwRuut3ajL!&i=cUWXX zE+UR6#B-I6bawD+IiF-D|5$EhFn5|4y$T%`L5$~0<7gd~Qo;=NQTZ$vicBgnFRq_< zP=AWmC-XRatzcQ7JeL208T90tuI#f(F0Lw&#WL4?#4TwVaYdGo2#X{=h$>4lTX3FA zh7zd>o&K&r-1wLMm6mf!XkR^=k>G>bt0y@%>uGTbc_nY`o15-5mnl5b8}O$6x6X##WlWZIiI7rMzmxLNrh)n zFzL9(Z(y|19A{VR9fSI4meOU2Q!Zh3ew;od7+eYNAcQ&Idh?H@2H_UttWX#4t-JP_ z3+l+CBV<=mF4p*k3Br35Q43o@9zG$YcrH|WLGJKD+I@58RAGMf4jdzH9jWqdV}&ri zAS|qSGybdP%fogFc3_JeC3ZEXGs@Q8v2{5$rL5<|SThx;ha|}kPXS}5P*(*d*=HA* z_s6@FjePFK;deOZkTA(5ZlNvOP`KufU-5DOobquvCF|PL)eBy;Or+N|i`ql?Y_ty) z=?hH#vxXZM(!50^3K@h&kQDsEN($yX04k1{jRB%txtN&SS+IDzm^e9ExUe{xxR}^m zxIrWb$levQKGP9W?Pg=)3InmIU$Y9uLZimB{2&iuwCRH6)Cho`bEv@<)5PE^ZzK?d zPz{T+GUj<0UM@=m99E6LSW+Y|vZ(CEMw7v@*b2?6q%T}fuU5B2keumb@nu?+^Q1$7 zsa_Ky_Dkm2c&20L8v(8le$UT8@Vd!0sky0UWyICRP$;oY39n2MZ}~#soS{sVz{YUI zAOLr;+fx(CwaGbUO-n0}jwte(EVYrhl^iWNKAI-hSb>FEl?|qX;5qMv2Ab=rOd~Lw#z~}D z^XD-q=cB?>Las6ub}i3YNg4Q!_96x;N;U#yWSwX}7gY7$T)vJpvoT~XwO1e{ad1^- zdYws8lcL5FA2w>`%~uaeIdF~P747TYB^PS8_g{v~Y)W)l4OtIeEe%5zfk)<4bgWgV zv7MO?D>$VI)2fmyHXG|rSkMV6<9m7S_8*aBhEOy16W}Im2P+huJHjIhXvWlW&XzL!O12;Bme)#{qo2`Qb9hPrST z*+%r#6QF(Xbj8m~hi$_8o|ZX6A0lSVEvLh;E^czQV$o_=c8{o$R8j-N0Wj`onsQzx zdPJ%Fh>xUxvXYksj?FhK1>ZEOR=@e=!femqh<`4o__12Efo$^j110Dk3@dNTi#x|w z{pY+$zXO)5V=KQdYpsT|AbB^o>38uSt_{`sD+H(?gP91C&-2fOP7SP!YjqBmnU7Y0 z?RKw7sgKD?S9Y+gpcW%Q59lL=Rp8eo*Q6cf-?-nw3U^;4on2VXw_TuR7e1d`3qToR z#1~Nv-^{dlLfJe)tzRprHg!IWOU`9WYEE0@7~5f0+991Xhd}AomcZK6NvqR3p{z-W zRyfR!NL%?yq_BZmW`pGu^b1(gqt2kL?B3G6I^pw)^*7~`p{cryXNq|Xuv%oa z9$?d?aG^VE-smOlwn#4?vF8%1=a+Q}+RJc2@`MZS3Q+222V8bWP@rEfM_{>Maz>hb zJ?#Zd$TnXg)Ia({!=Ob)D&jms#+f?`6qMlaamF@70vgafc3D-&e2%HyZK<2(FOnr8 z--Iug^$mA@pRsHspI{hHLhubf(*=yTP*PhM!#vjsi0#%(Bud5QoPG}4BK5*0ypeG* zT~gX*&)S;$a$F&?{OMYH!|`AW6CEl1l)je0av)j6 z1oBXsGN_GKe9%3HgyP$73(XGi+XN1O_n7u5dR{(cpeNBomSdEUZ>R~g<4TgkfM#>K zk5oBv8c(^V+QezQ$&sf(3C~g#UsW&-MD~~W(^gvxE%kIU?^sntX>6;bRnwQFKJF386 z^Vo*Hw8U|3v;~w;#gwd=QDKsG+|*YY1U*p4cJG2sru9B_9!yi{>4ER1kD6_Z%F>e* zW@^#u6OI!V?#0h*6bS>%46x?im-8L1zC1`IG+&@w>shZ_`nb0{dewxKiOmhEtn3~l z_JR-_n_Q(op7`mQ!T&WFcI#N^oYr`)e`nkn-$#qb^OSkSh z`BsWZ>Uhl)vfhU__{oU$tziTkFya%u=s6$2#qGO%54Sx&^+y*-6?lGMEWMXgB~2Ss z++VY7vtP}^yh+8lx=8406nTbpv)F;&4h0Qd2TFG%To^| ziGY|48+d7mY3GcYv&HMtbv!sqLN8Pa*QTXJRZW%aqExD*7M@(l%0~vAnD;w;b>yOT z_p2|b;ik(U^yQ_iM4ohr(R5w_!mu_#iKWtRgC`*}@50`$*rwNjFt{7xL7STPkjz$zmbUq&l0t?gSa%-KVT3GY6? zk{9Ezm^Y2xR{BH0pR6BD8~stvmcAT(Z3(`$F_aAJG*0eCAy@j@LjADIoG_0*g{T&f zDUs6-KLcmwjF6WzxoyVa&u0CMsG9Hs_dCB;d4{;&Iye`A?0cd*ISpBeNQ(t_j;8~o z%>qFa+J~Mv5Ejc0-id-aX!&?XNoR?J1h;@d0nPW46%CS=_)M&*BXQ^jT<(^$fh1>b zVG%MaPU6l4f~pmpKHo52Lig`pd+{B0aDfZ#0XFx$DYxt2Ja4aQK#xDKo1t_sL!x}X z(d0vW%C|^MG4LkhNbFcpu{j%Jw;x2c%8G$F1EG;Zqa>G^^8tEyi4n#%09s}#;slk* z5BGD)o1-OzPOwy*rpt_GBxgGrzbw8*ArM~nAigpkzCr#L_{rN_qBr07iO@*cFo3Sc zpckz0kQfYcu&F+4i&vSXbyV4>$|6l+nV-TUe)LE$a_}tR9-1KyNM;>VYNEDhiJt}O zZ8PK-_7MZ;$0brsj$Yd|<*!E4%^ERa-q0X2^P`o%6JN%=1lB->(@}B+#L0{TwOrki zrf?do#n@nA(<6`hp>s4y7gcSV>gwLt^Hww#7*H+DTJW*1CEXIss=3bbau^DJ_bGhI znjJTnH})i{*Rx3tU8QyU>=$atbXE%5j!8?qMEeNHMQ0LS%o?BmjnfUe5V?Xj&+5 zp2fY!_j;UE$*@b7wXe#?Yj!aqmTf{0nknj7+PcDKY4zaN4?%mo%nX%|Eqz>|82V4D z{2xlk<=>3zkC9HFHj0+giyQKB<#->02~NqGsN2a+J_QrN`Tcs?*LOa#Ff>fIGZ-D? zG}QIhnH)o|>a%eo|8%QsBT!}J=ww{}(<%K8DXxBaQu_&RYDW2)$LeB}bNJ5%d1TfB z3*||=3{|)42Lshgz3ur@BzaHu zITsDB)x7fbQp<$qG+i}T?K`L+t2kJEh?{V8>c(B(oTM9~=+t7w`^?&>f0V$yC_Ou5& zEqaqVpVm%FsKEWYEDCPMZIAZEZI4_-M&A^IJ9nl2+B(Ou^qF|9&SM^HZLxUbk^HUl z1%=)V*4yk|_bz>0-(K_=+#K0EeGwK-L1gr;n(jiYWgIx&Vx0+a*dDGw&qN6eBKKrL z5u9!DQdtSwep$ubg8f9J9fT9X^pC5xrnw;q>SWFTr0YUOtf0YGE8#M=##f&Svwl}t5r2~SIi8VdlCf%It|-Z| zxckB8_MXe815a15N?>x^f+<-Q<1F=1f#G1JRO-70qEOSYO`71BHjw{LN*G&m`0^xyhZu75<@m61JWeI;?*Yp>oTvkmZfRGUxMAkX#oZ@xhP+ zQJGi&Old@MX7k+VpB|ue_jM&#O!aqetUY*$b8l4J@nL(K8ssIgL-`?4Yy=MF&=-au#{RX}R~UJjZ0^u{X^tG4Sg)ln%9;F&bX_eQa8XNKZ_-@pZD4_2;QmXG;ee2+^vWfe=*GC%a0Z2SH+p5TUi-BSBmWqsa*5KB;MeDTDd2|<+SGFH z{i&7cTNnAGDPvndVrTcut5NmIDVzX|)f$-h&T4URD3~!>yL3wvbSkV{$z3Y0*b$z5 zUD)c@&G1_$UnLY6R>-Bzq7{FNOH-h~Lt19a2H3yV(7w%%=J+a;hy~=9OYKiI^s_)8t z!aL~_9jT{ihQUC3S{Ba3gSfvqV3t9?|8<9%X#V34o7vg?pRO>tb^=%*G)VOSetV&G zh!4y@ObE33Z>D}oxBrxa04f-8JQW_~0}aIB(h*FsH#{cle;T6^e>Xxnslgn>1i(Mo zs{cVj_5}e>NdEx;o4oq39)W-G&HiD8jQ+O~1vq^e6Zi**<{!W?tiOSX;G$7{@cl3` zpp4?*s-J}TzYkq|gcy)c`MY|Rh#XSI2*_oCs3hQv5o+LnNH?IM82{+qf*As02J4S9 z0@Qhbn`e!Z0slzt`~&~?^=}XcD+c&+6chO0sTYt{?EjX6fO+A+frzO8ornS68o~$u zvGDz0o4-zFhS=X$2w;gZc7U(eZPh*(C zKVD+|0Y=gP8!Q9}HTx~HX_A7g#~A_1*1y$@aS1^D`@cm5u>p#f;R|w~yQV^iv{{IW;fQu%007>rPwFv=0zdIP+nhUHr$q0Do1rdqB`MuN- z6%#x-NeqDc2zK@cf}bZv0m*(~(J2kU4d{1`z6&b2dx{Yt7z$CTz!=j6z&|2F{~Qiv zUl2g|2?7!VgQgh)V-XOM9xU<^4ZJyx3H-ym{llg+`gg}{Gm-$@gx_M?3=i->Pq|P~ zLVw@~5`TlxW~BfZ8UKrqAptYYVuG#W|9f-8<0no+2tIE=_!Hn~afTz;GRa9`uJU`&10^Boi2oR|TQ!JA}d0jDW3ih)4||owwr$(IC$^nTY}=UFwr$(k&w1DRviDm38@j8ns_LHt zOQ`{?rTGD0zG}7t1`Yyp1`Ps2mzZaaktp{A4WKu+F?4p0R?}8TRY&`ZNjEXT12+~3 zj0j{$p$~6bQmbv0>iYGA?uU)YI>IPXl$_bz=z#P!ruQdg_fwI)ZiO#&WA)nN@>k?n zB%kGT`ltX(Kn3kmI`jL*`tzml)4{d*KYnlr7=FsIy?}rpGGiXnL!#hat%W;G)s$&{ zsz99#4HQv<0YrmQ+mipe(48CPk%@_E6y-@@XB0TNc^&2cklescOq33!9gMrQQ5#vx zI=+W`ueXPO$dP;?W&b!I`{osy<~#L z(A4=+{uS0<7k$!(YC&Jz#EKlFuFFHfC@}ww-=%XT_ZDE=fu5>o=F&gb-oEc-{+CDx zSv$lyyl4XdGdr8jwIXe*J;o4y*f-p;gaMnJ@cC{wi&%7JT`XPjR#>l2I%)jM%s52u z`=R_w;mLE|=@radcQk(aPy&0r+8NaE0xk(exSC5sF?yKZ}1h)9ZZRu!xv#fgQ4E z_)#~yCN`;+Cd{Maur-nTTQ8Ytc1-W`>h#t8bfU@Z6esXY6dEV?_u9QXrF&60o<=Q? zmb8v#*HCR1=7in}C}LY$$*>9B0AEWXn|*zS*0xTv+hRrp?mXK!wf)VefZOcfg0pAM;PanH;dE9DpG$QXMLg}e4N&ZKt{h<%SSkSY`TzlIO6#YZ;rk(H;MhujYyG!8qm(zNu zeEHGmh}8cu%OCBh(`QI4&~MwPm5UnxfzIFSb!WsLg^Y+aOsVvfROlb>TqA7!Ao$+_ zX|zL@s*)Qfe+)kPee^jl<&r*r@8l^x5=s6QXBhHvthiY^pkVnOar7kN^cnW|k?G`b zx%iVzW%Ez-F>0adQ;L&Gc=6{dLje9ZJ-!4rk69_!G8BlAv^V$5>=< zSD!%gqU89Sr#DZhk$=YuKXf_w^E6p)W?s4sI)Me0SE^Qi?hCFL!^NVoAP*Pyr-p^B zJreWfY%h-jY6r4cNLmy-7WWOq=`8CL*Zv2P7!7p`KYVyqh6@S;BJ&djgynzRvKtCI zk$M>mNLJRBM-@clZJ@K!ZPrk?6+I9Z*vNN_)Sq2Qi^D-lw31Lj>7SpGTVoxUW6sig zip+ z6g-HQGVzoWXu9DR_s8CNa0ox-*4z;9>=+Ij;Qu!mM?0Qj(5eA#eB0jDL9&3`jjA|M zF+v^N+zK< z)=ar{C-`C@{-CL-0hBDHtbGPoUX@_5lxy0B65E^$1tY9tTNSAnfy<`%$lL)`8PrrExV`G%B z847+ysh#*G$nZz*M->ZLR25#&n-nfA_FL?80HGta86PhQk4_P5IIWZK9Zf`r&J`re zb>{)mP^aTGpU4?5JzmeN6Y()J{0haYyvvsi$~)Lx8;K=oG2>#YpH#)-^I*t3`xwEG zx9*86u43;-T;313>IW(91uufs-JJ}7QzZaIK^p8Q3B?lu`us7yW>bKB#Ql%HFx z&^^4=UYC**mVz%L3o2lloGlbs7ogf{d>o5jBma3O;P0fIdq}A;rErB|q4mt)5b9P` zC2_9LVt(C+lwM*V6tn5OC6KUa``Vm$9sJe_s9y&-%`Z`VV`+ zezyhQhcE1xA4S;q;5GOyoS!}`kn8cKzzU!ZAbiyMGhn?W{z}=G4}{vC4)7q*SDDvW zn#{?3iTt${`%h(3AN!^L?aLKude=VSMcYS|6;Kzo1nl<+175^G68!jCC>U?k@2lO$*yuAV%1%i zVNaE1mm$>fCMr|*yJ!wcAL;#8Q~BdV>JP0^X|^Q0q$-jw3QG}Jn6**5lCeY;@nX7? z{TLINJQfPfn3JaAm{ij_wXhm){ZJB6OJxnOX+HM$E5sy_3RkvG^b$9ZynZxETefX7 zX|mXc@z71VIb$xDZN*HYfJhd!f+pT!W2!YvijzvPbXry~n=|f-JSzwjx2da;rWE&r zb8Fta{CHy~@5@5n9&xpOdL8q!xiu@zBGSu_ma$G=VLn}^kcWF0e)YD|SgHZYZLazE zEb*OZ@t||8Xh$4ZEp=!u=sLW!+aLNiE!rVRSpE}70%jl)6&2yI>RhfJ)fTmy7sGvx zzU}FualhI1Trz@@lo>WJX(lg5v!sIJ9hlFQe&tT+ebkUO|HEW?j&HVm6NB#t(1OE6QUs@ynOE z%b^7h@dwXD#y6l}iCTH^AR)t-{is`$c`240b-ymW@tjr${_<%IVo(M3@nRO1;^Oo# z>yd_BPW?Op3vZv9T0uu{0G;gUN)XXPpQjr)GPqMc$J) zUNF^|aj^H+IZ+*S+zQPtcQ|L~(_6i%qn0VhdF2mHky-}#A>rktV=6P4j&PPJA^*rN z^^CJ7XYaWwXVjNzLVM4-lrBR?WZ^+-3G-1nv-qA##ZfC%=Fio_;flIHTG*3D zE1f`;_;)>4MX#WTGiBydts`G_%z5N2ZC5$(v}4Ss?r~Q0v|#}GE|TLm2~pkIs|#1e zRXd*IRy7PqbQG%T){-#BT$1srvwUgJG~8aizJs=43^Wwd|P$dm^Mz^*70 zd9Y-QHxX8yZKv3amK*xc{yzDAbJ{lbD-waj^Fm?F zI%YK;S=xT13Ce7xcR~f(P_itinZD2)Ls$mG777HLp+erdJ+eswmPDiT`M7sST zjcmARJq|VHL86AtS8AQ&kklAO*6eCH>CKkV7j)k%9ZBkiN}+{e zg;L76$|s^06KM&YXCzs_(^>+vlNyNhRq79E0tLg`2-N8W=d^|c!NnD+Y2-y*)k!Se zt8P+2s*09IiOcBEA3&%ojNtCthhKjFf@PZa_|9J&Ad& zfS4&34w(Ual*BnGl32VEjKr8HY-2@2>$XwsZ4ZG_m`-|7Lowb((4a9{vvh!LbETg% zPOGaHf@`j>wR%QCjrTB15|%$URs%&@Ejtx9WdI0%Rj65NRcxi{S&2pc(As6hSnCw49Js8}S(k7OI`LY}uyKm#W|12}ci2 z9O2dWc&F0oN}7UY&1>P^%+7p?lyvIri$j-r1fi$agg}sa1&8e7*WgE>RYz4~G5vnu zRt$9NlK(CnDN18+6WK8?lOR47RXROAC3VG$jAIl22*bnW%DaC=iKo?Os%lG6Y#IbB zO*Z_o_sbBZ$iR!ucAs`4`UaNWF*k)FfpCo zB?I7O+DD~VrI*|HB4VdVdET8AOVv|&+okU9L)?X>D$J;mVlA`S3#MzyCYRLxJ-snH z@dK!axlf|jPdQKl-C0MCzLMusMI+nPfy(o<`_RpgV20nWhO|e;4^8(hu%pse0D#7E zeC;uggQ3sT4}WN{V^y7UL>>WF=hmA|hS`c#b>>bWb??S?U0P*&#d&zW(A3yS+FcPT zL)U#}dM%Pw)S|GqHK<)&QB(RA!i;aFp`U=#wr;(qx-}6i&48#`?$tb~q7pcxXD3R6`V6F{XQ$H2rVi83>+L>HjHe$Dp^tY1yH8xIR#->}1H5+ooEroLPLm zS0#LTM}f3WrNCU_6{O^(AdLVGN4%V5^>*uX1N8^=847uCz=(M9Rf= zN?FOc!k;1IxvEDf_DyKc6@Ux+P=+s0 z2c>%DRastkD7!iDEMC>#;uYL4w_2ts+UBy}r+wFLGnCpR=fS9b#U%S(E^*kre)mn8 zjG1ec06=Z1n@y&RT1U8RJ~V3FXC0jkYwQ`_feQa?i6Wd%ake^zq7AoMUvM*PhN|iqvZC^ANS_ z@Yb7bXgcUA-Y+xMft17-Fb_yklP;|?%q3QHg+#&2vZ3N5&4tPJw9RLPtB%M|?#J%< zi-Z7K*VX{Sh5EA_;k8KqiSF1W%J=Ir*ouvyKVPdgA&3QTz`W~%d2J={@6eeWyc9&` zn+VOy3us6tjm0Z#8<=;qx(s^#dAlIrJcpGL1KX9<-gdWdpdP|!%!Yl@qW{WZG5f5X zm(>thnK0PDxhju1W^~1GF+uW#q2Uja&C_OXwMEDb71lO$d>^l{Qd85ddtRt4U)nxX zU8JdXEkEO(&MUh`Erqik&%I3|=khL>gx(O4@Xb(zsHrS}CCvb}fX+;&sbI43+db-d z%t(RL>i@z8e)oUDt!dfi#wT4e>@+{M-`$5~ATD=(BdcTC8M1#3 z&vS$$<`WW<9S<}9QFn}}m*0ow2xQo$?T&)HLH}C-V3gdt&TaK*BJ=$6`% z(i5wcsnr?azAuiokmZ39%j^g*)!o-dkw%YLzXU`9s!4Bb9~|C-V?ICm{c}WET|1+m zYt@CV_4?&!)pDewe5C49rKz6ksZ@y;Jy5QNi%J`(s&690ODq!gDV{4z+ef3V;VRi{ z`+JxHh-}syKd@=eQ@WlCoft*wBjXma?O8Xu5DdJLIs{ zezP)PgxD(^g}kQEbkumtpRMr~bNdhZ)HSlsD~uayy>f=mkl${%*K%K%N88z&8?H?X zEkC+;gpd(z`ljcK?uq#%rVw}gIg$UR1ulOA(XtII>+^RwJ`5pinUpkvzViKZl0NK- zt(-2?cu$Dads{QU*mxGCXzn+ef)o|b1hMriX@vyyE{no0CNLG@&dB*&yaQdHf4B5| z;y=OhR%0=-47i}(caK@vx^<1+260QxqSVl$R%FP#uNxGo9AV%cjvU+f4Io10SV9p1 za|cqF#=H5Gu^kP0=aW_(_jeA0YPhVhgq166V z9#n&Q5ZJ6?0~MDJp@o@oqezXeu}wEjgX#^>;|It>O)^o6OIo|QhOf4!?^tuee~P0S z9BL$IF1`*cOx}83=;xnli}k*sx70^?qCRS`U|#;`)o6V~27xK{rUU)Kpl&f$1D$Vu zS=6&{Q90rl+lF$YQ5g6hBAZ?I{}3}VItU2$|G>%R3MP=H0jYzgj`JN$JHZt7C(<^W zn2{P5gJ-T{oM7v{O>s%k=N-E=TDVVQ4|H$N+bo1fNn^2(^ zRvk^oU@Q_b0$#nVm3+VbXjOt@2+>)v<>7b8oZpX6PGNM+fOkKuKeRmas@$>pPj%+q?uPrG-RU+aXN%V)6!HYkHI4a}f&QFH8d2{Vd zAXx2UMc;|M(imDAl+KuFx0}Tv)r^^H1^@|Se7Wf;WB4((v$gzqvu#>@W;i;HeO@{W zq$9*BqHoU^|4#JVh(N=Yah8IMcp#nujByK4JM*T4#@Lk7yDFnPp z$jRU{OkL4f^^9i0*!-CB&3|_*eNAejj(g`sV|<}$@GR5vV7c)t{ixRsMJ&xF2LLQ= z{Q-{XgT_1J{4g3*!i7S}_ zHAhlTDfQ#J<-<^CMXfv=>|}pXHv1q=DwlB+5t$a8H*c;sa>c1~JfrKjv?5uP3uM(L zYsh99|gQGYX)V2^A8TwMgio-|W; z#4 z@$iWOi+@=rG8*NrRtBu)GQ=9m2kwu8FqW;YmItlbj}Hu=!B}u~oNz?km@;MU6Aj+MZG4a4V0{_r!#cq z=0F&?P@feBd=KVGXfEQ?gs;*Z#L=_BSJ~mh<5R%Cou0UTb+4zmnBnh@Yn8}5(?qxD z${xE<$G12~-O1CCW)RJms@zjyJIj6QKFi)}Ydidf6z9lupowG~{E_g(z_yA!nTklC(Y%Xk(CK$o|M9=P0U^eOcM9IFE(7EU?Z^?9-K} zxY=!VZjPcAgrJo$d5tG8>nc9lGZ$+Gw_%5Qjo5bK7`cIx+T}*K8g3GeK>!PW8EbW~ zk;X@?^P}vNfc@=n6{~YtWey<)!?6m_rlqt7u<<3KOpV+6YOBm?39mMRc!515)GVPrq4M}*l`(63PL#!m)B1oW$VYkk{4Vv~SL6q?82B|uC4uK#;mb<3!4u3H=w}bp zQdsK-a}L$W7rutwfY>wa$lIkOEeu8}&YNNgfT|BLsj5)50#(Y)G*Uhyv{AmpuE@7Eu8YyR<{BtKujsq4ic#(P zCS$acWT}}(ZHpam7j+(R#U<;QHtP$7F=K}vv1~JG?~=0J+BeknwvdGn7uK>8R@1=R zX+p%<|6|!eZ;9rJ>C{VdmLGL{VioGvoM3uy)owox3*hU>!q(Li$aH4 zw*pN`Q6ZY6EIA9!lCv%-K^>Spf`C9H{inJ9r?&*w zAQS(tLIGLouK=DYzTegbVB#RU#&4!)F^y;4EE z9aS2dzB5RyVN8?ZRUuN*Jq9MoJB!xd*K@5gx_9p(XqRA)!US(^j)}IQ-FnQ^laW@Q zD)(`nZBpAS&%nX!5`{vuBR&*i9;IL*F34~7usAP#|JP@Y!%m|R>9!eSsm;DfYEv z|8Qe5F(C~$n5)>dJn>P=zD^`1A5+dc-ocCBhZ+iEA_I_rZ$Xh_ydks)HYymnJH%Lm9O zsQPl^`BJ{6DZF|=;w=fh{4PuX`lXDeht|~d&M=N&K#rA%7#n}0HUDq}05fIbfiMj# z(th85Zut_t!569NRrNDth`VqwA;1~u$U(0kwl#Mr6~`|aleIG_mGuMX-|P*HU&XHb zTwg_N=Eey#ZGYm~0d~!Tqx|g5^(`bguyBL(Rk-W=q1ms0qYlIV>sR{xIa-xX=%oj~ z=(`^Cw&0PA2LksjT`mGP;MH5_ZSe^2na_U94iBV$*(bU;dX>@UUB1P^R%$XGB}*jH z{HWLwieU;p#(d)qbnS7Zp}7BC_lefKx!moK7qM2 zu$@O*KrZ-R(08p=KKgZ!+IC&$wpt{Ry4j*^f|t5H8 z&p=^?{V{bo(B_`MqaixYQRF0{{l_(L`)}f zk-hFl@T3xeEzlhGHYT_f3vYJBqOrj3PPU{-y{~Z<#2S0FEc-pcU=`(V!x=R-CgzWy z71}8&wB`94_fEqOH189?vI7NH&0L+;S5lZEVtY!XdQ4Ao{qoUVMv`MH2KHauwdYBg z{IcbmvYSsLmwseLR0T3;!Gimw4a_$0y5K#NC<4oeAiv&RQ`5Eu#TL4oS?xb~zNs~u zC1~AsKvT3(bfo6g@NAM1+&;FqRlBJOEdDe<=*V{4bX-USNuu48JgZ9xcruXZ6l zlVQ!xV~`jI|Ef0b(;vON8~vt-m#$xN?imEb{_(CzUutvWh@+xCv6C-P3z6Dqh$`1x zW8|6e@I*Q=5u}TfV#iJqXYfECa!Rq~rP?OF^*}t-*&gH284&V;ypWuR*QEKBwTko0 z+LI7}hPBWG0=$2DB8yQ{bOkPMI9VW>tAbLRX;zYIKk)d=TXCoj2jP|~8S-o)E_INc z7*|@I5Y)4vS=I^sQTU=AEHICEIVn*hZg6fj# z2*XU`z^qgKfJ>v@A`L1%+ zC5awM=Fl@q_=5$`8r*Jg{0zx-;!>@711~d#z=n?cmIcfPx!itswnWw`sVoFk(QWf% zFN;d{R%4H0v{tH!Na&`jzeiyJ$-pG8=ed84hY12z#^!H~*uVZrT}Bd7H`kgg=AqH5 zUpdpjPtEo$TG_-*@sV*_PEJWRJ_$KK5w&bGWj!+WtzY6_f|Kqu6iWs<8lj)W;p-v{ zc9_B(6662_7anD!~FRp%I0)YkEY6olZ8@Nd(^p51Y%RSY3 z`i_6R>wW_h5)pY8?`uSM-LsX2q!62GlG0Ml94Hu#H;eL#aaATdrskOv`#3YUb8^m( zicTHcKZ52_%#gIpo>1O!0h3Q2s<@G~!x^79`5J|hcX#a0B-p`*nRc1~N$)UEK%bON zY#D)0?>MhAs88?NqU;?Jq2z^y5cTdRIr()o%M$V!P;eT47zk-P&1biptPo%%Osrb#*9sA7R)j zJI4kqbbV4>DjnyU?2^o?GWGUdVQSPp0z0!Wi?!^W=B4<=ccbmfG0$jTt?9Q&36G_i z#djy|tPbd4tnKW~a#;M>s}cz|Wo7@K1A7>r%~H(6leTycx1RJ{)HaX}oCAi)^m}`5 zp4kjMqUbSgWvXavV?)2+()Q7z>h}?gUFGHA)P&)`~_A_6+FS z&fP)HwZR#;h7NhVZ0d9uM7oz~ZTM ztwq4rDeRE3zwKLufI`i~>w`umpa*MBZhx3gxq#>TV}vvHXWtKFs`)l>ed6U*X(yXz z*k$I`3Sz%3OB3B+mFxq;lU^=WA%b-R10~?g*0X6a8uJXWa8c%~m%vkh(77zbUzugN%h1Ua*28eq z)H}QW$}lR|H(T8J_$$!iBb?e3zC}N71atjI^Y=+4WZo7_NC`Szp;FqYw~ehA1uEo? zI;?ba+ChVmL-^>@L)ulOH~=Q5?#WvV^-B#>>oJXpMtk6-t&G>I|I!i6#g#5dF$CxD zgSC5EHieib61EWM8GknHTY2Aq3aLj zANvq*Lh9aggq?v$^w_QIbAvGYA&2X(WZ@;@5bf2-?xA7do=~@N3?9ECaq$_R?e=TBJz*~#)lp`+Q?w|{s zgcKRKE6zi6VCNSvdI2nGVS_*W9P)b?e#xcuSo@T33k6^47g+nz;0(b5q;`eBEbO|7 zQQ$2@4CQJek-G#+y0GIpWKAmyki#p+_x*E4!`IV?;Ie*^ynzJONVRS5B_&UyHyPSs zN4E$!ohomrL1|z^`%gKX7f_?XSQ@^hMxe)A^i`mJAw1xQ*#b~O@kzg0t8#DfMzF$J zE$TVeYam^rvuEMP`b5Yu*b~7uhEm6uAy)IbcDT)q#cCi+(Y+!^krC|?1wP8Kb;&pJ z5O{4wI+QRNo1%*j!z5Xmhy`JeBnCllV@cyxEk99?gH!|(eD!eZG8}jcOF)|@O~$X5 z$+Rig6a5+NPk<}(P#FEZ8+Y_>6rwFm%WElEF&R^E>_8Q>C)yK1XEs!w=3I-}s`Rxi zKIbCh~3-*F;hlaVR=YN1@Y2`Glnn z<9RNzU#BIk@)pV2P*u)vnA53)=J>;VsPOrkSC~qlJnDgwaUU39Ur%v54xH$@C zRFT~!b1kP#$!;N52%?K;eL&E#Xo6Cn;7IAR!k+GJWD(dZ_mI@!p@1L_|&a1gMS=8Hu?iHPp z*NU%tUu7)MWu2Kds;}k`5Ke;MW30Ee$WW(cX_JkIF3Je@Uc5V5>4cf5kKzw$?0Afw zl>G2?NKaO~^fHmed19m)$)46ItBckmopewKdO#OyILFFiR#wKcYKT%k@U1#|oTq)5 zbN`uN0;#gq|NPsQiyAe&%Xo!&58js*L1k+kvD}4bv-nQ9Q~uD-B6xkmih73`td@Ol zZ*o955@P;Eehl$AG!tYp%2`M&wMBNl*gMme_ky)ip`*~&UcASGW*jjS+>_;ib&Ulq z8o0_zuiMBzwT%_4ojJJ^n%AvQ1+Sg^*)xW+kF-uBTEUJK=#)=P0=J(^rbh>}gdzwB z!q_5B``j!-LR53antz*iGESW^Ci2_fOYl^5Q)PED_AoSYc7{eZ2TpU7D*7GeQ9Ia@ zGVWF_Lv$tXN3!=o^ize;A4KYnio1TJ7eM?4sooW6tc((CqK8hW=9jWB{lMa{zC*ox zBDE2#flb()^mpjH{mCa!#7$T=feq4Oa(Q{v6iiGhY6E*bt^SI6gQVc8b!h&gnGF2L zfKuB&`WShI#hQjx{9*vIAOz|A50E^i!}KwRo2HTLd?Eku$WcqMaoS}41WV;^pmR>? z#WWe1tSS8{|Jl)*7f+NIkY6srbHbj5)kOK1L^H~3{k=lbUYb;EH@Wl{HyEh6v;F`< zxjoCR9{ee%$9WNP-rMX@DG&+Wgg|E#Hb2NHZjj;-<=S-PPKH7QTM(iA>gp%FyIuC- z{S`O{9Pk@f_EW#8_$6*Dy-t4sY%o(%WHD0L_uSO~XrNwTzgaCah+Nagt|bfKGk1*V z49=@?6mp~G5nE4%jar`vV*vHjoCk(v{+$YyuQ+@08y>Gp@$k$GPl&#l1imdq{j}l zN${qoyT6`)+0hJ;#n=7cuv8T|ULuS>awRQ8zv{rTV_&z7H3KZ~!*xb|ir%CD3fg3r zq$hcNia8qVos}C8tJD93u)1sw9@oL-Y8rSgXWEw>6@g7)-U&BORn!AN`UwR_uUlz@ z_#;$MKslfm5oK!1VT*wW7&?r^U%7|vW}|1Q1b4r6`)4L08H@nyBnR}dUw{WYeKL6& zz_Hf+3dN33{$yut18>5v+aEwKU9@0kr1K7+IsoCW%e6ZBSU8j&oh=$;*)$bN@S*hA zkS*Qp(VSm3I)c5&V+mRr%FCD>@v|crJBVS%SfP)B(T0oXHKi&6{P^~KY><)<+?<43 z5ROvz)!2rKdt37&Y2aj9bxWCcve*u)A8>;gcZ$Dtc*up>7iQ`81=}L#xWS4A>A?2K zqv}_kVo5Yu7?fZ_2U-pNHC*doz+f{7o4H?C)J;~5&`iqb`r?0&_2rT+vlJi{viyvUGgodjkfY#4RY1kiN_Q=ELd~M@?#&4l0>OynpDE~^CuYFVg zBAJD35J)1G%j=aErK2!zxpeJ9tIPW#!gZ=$8@0_#V$dGlUbpHHdomnyp55wQkr7v^ z{nE^$OiDMy!~7hSbgUepbkwpNU1BZv)0f26uz?6~mw?O%NY8S&o*-h3J4KmyC1mRG(XTxV{7+&n6UrDQcG_T5zMI#0T=Y{O}D-qaRSm&=uiy z5*qf7!CRgLC=k|;P)qE1h7GO`AMhL1cg~FzYWF$2N<-@-y+${am~B{i*M-mcaP(9< zW0Ud|d=F@;H^oVw(ztyvFMm4aXN4r|;Yd>ihMuwn-`%nWRu4$TizZg*Un8g6_n~Im z_o=45lKZUAy!*cW@CwTOJRk?3E*jiqsVA5$4apA$EPm>Vsju)p*^QMsj2O!Vqki`D zPU+#Ss~k9b;gJm%@g}Xy?h_PrLPFeXLSaV=wEgT%nG>M~J8qESQ`hP!deMDFV+VDVg;K&o2QH78?k3rKvAt&k~1vHUvnw> zvxKS?#Q(^Z+*`og4o3az7INVeErs>PR9sLog#DSc*di>s>H;xY!hJ$NCm@fq4^=tLWea z=w~SrT1OvKKKT*+R4e2iUhE@SaGO7)!Y9OxR{jwcf>=8m_@Dr4{122-&|DZ zk7IqvJ^EZ~4~u{HEQLw)21enNExrmy0ESw1q1~Ck(^G*uD>?-)T7Vxs_B-ae00M0d-AnYQ*+Ld;AfoQUm+JgrbaO1a9WVN!t3#0i~^t zlh*~YX0S2CB&x_%%XJC3w^A(?Q=Jbq+lFAj!9raaCBZ-v_RV&iM>goTCd9uoAS6e} z(kl_wmzo!}3{Rsj3}IKcutri+UpO{nR(fk7v3{auSSGe(JIemU9!Vzk-qDMg=(xfo z0!;mBPKaZK3WX8lvaroVg(ZnV%bX*G`wXt0A{QgDpAh#slEeGOE0_|FD z|Bq*nhQe(xWtjl(A3L~)U74jIfX-y!Q{;^wjzD&!!4I7ix%vA#U#X0-AUfSN28;N2O+{&MCUKN^qsayBNY*z}-iw@fkWrDxP_3xw_K*Z1wqx~Ph6^A$?g@oXe#b&N$YS2=2 zgW|aKI7Sk3Kv6obLIGkt4|-ZQ-g3Cu6tDHUQL+(P3iR|7+H@Ysn+u|jgC^Wj!}YFy z@~x1vhIh>jvm=<<4?~*|o`5mf+QCSN`qa44>=MqI>{<@Q^+V$&!c%!9qn~}{2iUkv z6S?fCbjp8%skmti`7_;+euN}mI^*Qctn<7wBaJ5Hu%W!ZngTZTcCNAvO;Tod4F>pe zl@8e^!616gOutYY@hBS;??hTrUZI3L?IL;@^iGT1I2K=J!G^pj*Mu39s0lh;unEi& z2M2qbFY|X(Ds1m5W3SFS#H20AD#W;U{+~sNamjolVsB6nd}6fUKNt8y z_V@%J?-7+(t#eaPUD!T@zhI(mg$!>9KH)J4gFB!2KJmGQ&8bZF!6MWkIv`xYAJq59 zGmb-){Y1XOEeFtWsB-2hgZb1-AEAlcrNHtbf4E~g9AtG6(Y2$iz_72r^ zAym4ylyh6D^R;d9jd=T|tm6GGHW+yzol_zJolhluV0^_}On!F?K*Stb)8`BSzm~L+@y)N)-;l;`MMh6b+faW<%OPWRfPE+xWIWz{^ z+jkHn+Haae5o-5<7v$e|oDQ7XOyfki9L$!B83yq~dkm!v{YJ-*bVELJwAB(nX-hQ! znCjVrlbTCWmyn^KnAFjs;=f;^78;w23r$w!so+CL(wAa~Lak>Cg&f3l2tf3 z3Qnu-?irO}pU$&P!E`WXFw@+#>Dx_Z&P_m9I7d^dQjcYwm!+hHR&{{R6o0813m;s=zprEvw(zNX9?Do)k*%ez*k^Z5oI z>qXy|l%OIMpqgj)gty5@<79N)`%Ma7YS7?>2;aco3Z$lGgc#MJeAAeZpR%qq`#;`3 z_bLAP+DsILVUl@hFmFj4>%DsH7YN;~Tg3tDzI{Rk6jP#=^)C`+cb|nWr7XSU9agok zDM0PkhzrG)#jKo(PoC&M2GKf7bayjROY6?n#ye0LcAaPoT_SR%l z^XoK{pY}@p>t>R!d|R_C;7)e6UzHIS2-K`9v>b3il}J3n(F;Jwg8nRpNE_AxA#~q1-s;WIGW0@|KVv*Y8<$Wxl3^J#3P-Fo=NRz{QC_Ue zo;B1^zcuN@xwmbPn1zQ?2P}Cq7u;Na+oeU3^Y|iNY(eVtfwrm7Wo(Q5 zzXdXUsg*s9|Lk4u|4L-!|AWXpI_Q9%Qd>dhH!|uO3 zy@;YSP;qeSYRum!nvK>?F4hCa7_{J8YxQv6#5Ji>Vv@b#kBH6GCT(%=R-PCxY`#S+Fn}`+?Fhps2yLi)$sa<7ohueI?G*f7FKk zr~U}TX)~2Iz`RqLX|l<$TkA}PNbNel6e-#-r@NH($mrE1D4QuDzW<&FUd3z9@xEV& zVsCQJXkXm2JfX9?BDwA#JSEWp!zGOdl&QeGgS6y6n{DlZRo;0%7beJRje1ym_1EB6>y4^?$Fh7MX zTcV@;WR0!n87mWTpZ*+*NgKK(u^6AI2VnO5ZKIAmC`JCCF0KMBsxD~bQo<5TFU!&) zEiEnGAt*`+(hbrLE7Bo#>5>-dloX@|q?BA(8U>LQ5vBf>-|zc={=Ivjotg8_oco-+ z=RSANnHl#jd4wFpigCmD#)8GEk^`n;nLc821zYcIRd=$F2%l$PshAkVK6m|+n==u# z1o}FwE0oql!*wJ}S9(ONS0i9-`*q<12chBd77yiNpW5gBc^pniOeSigg%=M)xci5# zr5$ErLEPeZyR@m3lfz#Q+LPa%*#{>EMLiiY%f{X5Bb2g`Fd|Og=b)a@Azy^ zLWliJJ%9Of@3cWF|0mAWob*SW%;u+K9%MvhdY8v&?z`R&w3r}c<^*Gl8VyCDC)obfKdpH-Emf~6k>&s_e2-_@8 zw?A3^+1YbC+;h+sg6;QBW{t+%p-OPEW}KKa=91<1y@wCG_9a@6gQ8T)-0|C_v!%xY z8mQsnE@VD^VB(1nydUPZeV)@v(!F>`W}XIyed`jT&2@-WKn5O8zu)+R+9&(#2BFRTTIWsHuK?I1vcE1+}ZKy`$Q-R*iO!DOMth%R#JszW+om=AF{j@ z7O|<08pK)HLa4uPZ=F|+ANcgbwfM`ao~fkq^!H-S`Hk=B=-t=XVU0lOa(Q{2jffLa>!$0vxIHp9fMoHEXa!BpXvOW1|pjM?%IU8^XY^g zW|sQ}TZ9UZM_}ysDYM(P1J?FccZ9$ zY)8?c81J%4D@+t^T*^Y z1;U>UYix7jY;sta zU24u8a+I7&H9U$(K?htLK7Ju$YkT<~@hXbvOj_nrezVUiAoip(b`1X6_gH8Ho4a)} zc5*h}bkOP&x&cYnOA*yPEIO^I{$7-9Vk6+!`aC$o?}-v$OOE4i!U1bd=t1h&Ypw?_HCu*UY$ZvyInOhpweM!?3>T`2* z*bZzxGd~P{GY)2Ah@?!8A;*_h-CwG!@XY^~w8PZ#d~5}5S|H2He<66F6M$op zU2l}9Zwh8S7!z~1{v6;wmofMK``FA=jF0JQW0i-xme@iEDEGsmF=kHZbO8z089AgGb`;ThWNn!d$(Y{}14p@P#hR)MVLTVJys zDfQeV8zO{_>y@Bv;;5<1shT-1D?fST7=jLd3q>ni^4PhNA68QVytk<$$wpz(kM)z$ z+nwqfFJ01B%s;0TXRc)$G*F79sV5M0aTq@useS&KyR2wx@2kZ+J3Lx24{l4Bh{zmL zfqo?n>`Kz)YBIA6ALZ{KUmuU-K*wVKs1t1C>`WxXHW<4_$$kIRclnfTUhgrknnB!Z z$IptZoco)@)NiE4^e7FhdL_2zCB&Iz>Iy1G-qRevTK4I#@#586rgIpoA|IPC($_Ut z6;vo|8DQXnW|b7gITQpKa(ehFR3V<A+9-cG@0IL@6$_>*NeZ7f{9|2D9kdJ*9XtvC*(qQcOWQsfQ@&A>uF1 z`CisHt|QIwHe6n2zJubias4o{AWn{KE3_UFYq0n={`!sYtMW|$se*Iyo@KF?Zf)J$ zFWz0Iw4T`An^5XJQ}Tn?+*2VCDrn!2{Dt$|>7!lJs0lJ;V)m9#LG1}qJN@Q2S;0y| zxqh~LJ!~YV_iY(Xz=JCEX)f64^z=-PUW;a}Y53&N{T^k1w%;VVWBYxbdHo@Kr8zqn z!TY=?y0I)5?DMjyW8Nax{HB<9y4+e7rayCQg66&Sn@K63IpSzKQjTy(+{|i1MTv(W zqXw5Lk%NS?D7GPa*;^!CwB1__RrKHPFu@xcgXav~M3r@edv*wFLI@_+@!3U8tfTj` zRyUbktncec;69ILbpFyj-@n;qg;XAN#FXkcjB*b-&XUIS##VT*eMfgQ!Y8blPvH<_ zhnR8kDM##Y$@CJbZQue_KOB7nldx!DVEG2Rf<_h?149z14Wb21S8##z7jy{Sc_s3T zY03)bnBL~}&^JXy!Z!lC7LrV}Sg zx)lG^w7JwFFCoPxW@;Xy!6tZKh>|+p%6@t`M@k%Vak>r0aQ9)bxEXV6Gcl2*i%D54 zJd~B%;4#rEewAAQ3*|7J3v`UAC7_-Wt-^H)Su2S(I#KAym=--0=+^(j?y_LE+0-cr ztECRF4M3F@aizBEO?@()QmLMH5T05XqA3YdZiKa_FdrqE!Hy}yX@JH38Im~Pa>BGM zq-ny#Z6om>4m16y(?hMEk9Zypl8(*@H@4;*hU+)QCglx73?}F&dCn27X2Rdh>lY3O z^A-d&QHGUeFT%BI6t$CTtKv3`WS!`dirJ&}d<2st1(k+gD~V2%nsH-L98?QUjEK`= zqgV!_G^o0yyv#tA%4wL7t3_)#`OXibrQWV;LkdRYh`YbNJaM&|TFAUZQo}HZJ*tC$ z!inFg>e=>*a%Me_g~THC9p;;+lt%|P*aaLrQ`!%uyDI?yj$4AO{9JVI1hpoM76~yw zN{V$xBSYc4?=-+W6xm&o&&{$wHxDO>rg}CnDNf~p~cikVp4HO zi`|o^9Nr9cIDUpGZ=_k}tLVw?;v_xIPc$!PR|$-(e$nlIPo^oiqnQ$-z|?^fK}*Z@ zJhD~-BsA~bH+egca7NlBkP!6yo7D`;`r#0lp32|RTa??fyFL5X9_E*Wm*TEAlBrf_ z*}C73Xwda9miV|~OtQCkhrNgH5^s;b`;yi9P}GE^c$ZU7lNubR;QK0rURr0TYoE8$ z`6E)hkW~8&^h7doA^7r^;psk|lyE`lsIHQ?!>0mcX1N&dp#fGIAwNl{#P2{a|xs z`g1H1um}`(&{ZShj3XlGYAu4-F>mO!eX3)R5loqEZ-?zbs+*@5-&h%eDExACq`kRP zy0rJYtTZ2wL9cwDoTp70)p*AN)r&^$k0gq(iZtq>p1P)bSK zZb6?^?Od?gouHx6yWrI?vA!K};$^D#c7_#2=?hl+%QARp|94Uo^XW9!cBU1*8*^}1 zk58N4+$a?lpBz338#CK^2Ph(Q3V7gTRF92hE=jS~*0+~@-8his=N~U7SphqUbhd4K zFg#t+I(Oy`PWFRgIkym%56Dk$)N%_D$CNWk$0X{jIVyV!;XQ>k*~r~x#zNxHi^t?@ z(&_;m9af6#&&12#yn;*3(M>o-j1K+&5t=W~5rZ`|BC+^w2PjLl{fXE^^{r+zqN=u= zFGi%m>3;Rjn_6k_(q4oOQc;I>=idId*(X+SC&Msq(LZQ3oX(O7TdX!{n>ttRexURr zJrjP9C+DZMz`I4#(nYg$i*TE%Y+GkeN1339hfeQ>S}PUo^#$R<{xt4AQw_5Z@XDxt zQ(9KBQXyvFOkawxE;%DO2ILyEOGS1QI6$;7Vy0V!^2O#KNcaP*{Ux}lVUjN@x)!%n znmII5OvU9h8$aEa^6e$G@yh~C#U_sH<+`&-7nAniyu0imHZVx{dz;mwxJKJv@ue6@ zBW!nE-w@%w#E#5wKFZ?YDsh?;yO^DKXx+!8&T=J|?<7I!C+={gQj6(UkGqv4 ztQMj)1u`di3o*B?V3IVJj=i)3zabR19d9(ZLgxhNKAjQFF4H%$Z9P5^Z}@oc=^b|0 z`}Gt(9^}eWr5t7XMPzSZAk9T4ym#;B^}VlKTBQ|vD%Vc}rgf+HPcObYF@ivWX+Pz_~_B?k%*RRQn~Vj6Up5MSSC%1kX9?8>dq~wV|%97`ZjF) zXRF-?>b~h7C>xO|5l8j)otrS_C&xDD^M0bQ{ZioR6zVNsNwG^^F={z<7O0`PHr?Rb zN%ws&o0=yM?H>yVreB)okVoIR+%cG&O+NiyG1AM4dTiatU25aj>__A--lY0Ws0ev> z%#ad(`UjFl`R&L@^7LJT>P}#425hd-J(K4p^V=sD;RsfDNUynF&u80`k6){)-ZXUL zG-F8Mv%UdkCj1 zdE+W{IvU}vb33lkAp6~0g?bWJZhEO%P%~uScwNrXw-#KxbA!r!Tm5}&W%F3m0gIj4 zZ(ASy4dt5OOzT}ktii+j;`;ClEZs|N?kuIgrL0#ql7Wvi{54$cz7sqoHzUR~^o?Vo zY-=jDB96SD&}&zB5m-THGtMe4Q++c2R<`k#lzRw*{Y`$FBrk z>h(l1BNX={9$B7Xyj+6Mwzw8CTu`F|PjGQpchJl$v4pLjwI{sY$gCMD9A?RheR=N} zJ{9Z;tkNISVIXk)8=GZ~vFRDq4;tURaZy@sMvr`)maLojqL6}dL1}3o^9*-LG)RG* z6tkj~DN?Ti0Xm}}5j6pac3{`&0SZPq{pH_wlF#wrJ2w*k~1JyF1MKnPAEDQWvmiaZSp3GwRK1%+P=+`a}37XAyo1Nx?VMaCiF@c%Ym zFfc@~;nOv)K$4FMflVYk{MusvHDHm}UmzI(U4a0W^Dxkb-j%4yg7!*OWD6nqN2Ms3 z$mpt3YXKJ+wIu@x7dSz9CVxe&@axMS|7iUgb4wfaB8V(-3q=I#u>tO&BtVBYz_OqcF1K&_uAl|3IN-`YKUz7#)x?GW?LBvReI)odx_Qu>s;3LO^^OLUYYgyr#DY94!9hHqrod zOAw%883s}dy)yBASr~rpZSk7wX*e2m7tmgZ0Kf_iWEgj)b32;`*jeEO86;k@GOus| zp_i0bDBD#S$Rzn+apkHw$T{s_R<)Sq3MIG(136^;6_Z~5)4*l__vYAS|7|1yme#~U z4_{uf((4kSvivI&QBC*9($&-j#@8u9>qTfe6M(k|AcLE6; z`k<%X0OPpi)!=`b_`iM0_X0mAB>ptOug~%SMxrQ~vL8L;naQzn{s{Te2Q~W5+NS>x F_djzcFxCJ7 diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties similarity index 80% rename from backend/gradle/wrapper/gradle-wrapper.properties rename to gradle/wrapper/gradle-wrapper.properties index 7715eba48..2a563242c 100644 --- a/backend/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed Nov 04 09:41:00 CET 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/backend/gradlew b/gradlew similarity index 75% rename from backend/gradlew rename to gradlew index 91b478b7c..4f906e0c8 100755 --- a/backend/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,8 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx1024m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -65,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -108,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -137,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -158,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/backend/gradlew.bat b/gradlew.bat similarity index 66% rename from backend/gradlew.bat rename to gradlew.bat index 6d57edc70..107acd32c 100644 --- a/backend/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..96e914846 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'dres' +include 'backend' +include 'frontend' \ No newline at end of file From e09b7af2ab7b382d76fe310f3b6d93154abaeb3f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 9 Mar 2021 15:58:00 +0100 Subject: [PATCH 68/95] Refactored Open API generation. --- backend/build.gradle | 54 +------------------------------------------ build.gradle | 51 ++++++++++++++++++++++++++++++++++++++++ frontend/build.gradle | 7 +++--- 3 files changed, 55 insertions(+), 57 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 03b956afe..8a94c50ad 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -12,13 +12,6 @@ buildscript { } } -plugins { - /// Download task for FFMPEG binaries download - id 'de.undercouch.download' version '4.0.4' - /// OpenAPI Generator for Frontend internal api generation - id 'org.openapi.generator' version '5.0.0' -} - apply plugin: 'application' apply plugin: 'java' apply plugin: 'kotlin' @@ -43,34 +36,6 @@ jar { } } -/// Default OpenAPI Specifications of dres -def fullOAS = "http://localhost:8080/swagger-docs" -def clientOAS = "http://localhost:8080/client-oas" -def oasFile = "${project.projectDir}/../doc/oas.json" -def clientOasFile = "${project.projectDir}/../doc/oas-client.json" -if(project.hasProperty('oas')){ - fullOAS = oas -} - -/// Generates the openapi frontend bindings -openApiGenerate { - /// Source command: - /// openapi-generator generate -g typescript-angular -i http://localhost:8080/swagger-docs -o openapi --skip-validate-spec --additional-properties npmName=@dres-openapi/api,snapshot=true,ngVersion=9.1.0 - generateApiTests = false // No tests please - generateModelTests = false // No tests please - validateSpec = false // No validation please (as in command above) - - generatorName = 'typescript-angular' - inputSpec = oasFile - outputDir = file("${project.projectDir}/../frontend/openapi").toString() - configOptions = [ - npmName: '@dres-openapi/api', - ngVersion: '9.1.0', - snapshot: 'true', /// I suggest to remove this, as soon as we automate this, - enumPropertyNaming: 'original' - ] -} - repositories { mavenCentral() maven { url "https://kotlin.bintray.com/kotlinx" } @@ -180,21 +145,6 @@ distributions { } } -task generateOAS(type: Download) { - /* Requires DRES running on default port */ - // TODO Direct write to file instead of download - def f = new File(oasFile) - src "$fullOAS" - dest f -} - -task generateClientOAS(type: Download){ - /* Requires DRES running on default port */ - def f = new File(clientOasFile) - src "$clientOAS" - dest f -} - /** Custom tasks: FFmpeg Download and deployment. */ task downloadFFmpeg(type: Download) { @@ -266,6 +216,4 @@ task setupFFMpeg(type: Copy) { distZip.dependsOn jar distZip.dependsOn setupFFMpeg distTar.dependsOn jar -distTar.dependsOn setupFFMpeg - -//tasks.openApiGenerate.dependsOn downloadOAS \ No newline at end of file +distTar.dependsOn setupFFMpeg \ No newline at end of file diff --git a/build.gradle b/build.gradle index e69de29bb..af9946648 100644 --- a/build.gradle +++ b/build.gradle @@ -0,0 +1,51 @@ +buildscript { + repositories { + mavenCentral() + } +} + +plugins { + /// Download task for FFMPEG binaries download + id 'de.undercouch.download' version '4.0.4' + + /// OpenAPI Generator for Frontend internal api generation + id 'org.openapi.generator' version '5.0.0' +} + +/// Variables used for Open API generation. +def fullOAS = 'http://localhost:8080/swagger-docs' +def clientOAS = 'http://localhost:8080/client-oas' +def oasFile = "${project.projectDir}/doc/oas.json" +def clientOasFile = "${project.projectDir}/doc/oas-client.json" + +/// Generates the openapi frontend bindings +openApiGenerate { + /// Source command: + /// openapi-generator generate -g typescript-angular -i http://localhost:8080/swagger-docs -o openapi --skip-validate-spec --additional-properties npmName=@dres-openapi/api,snapshot=true,ngVersion=9.1.0 + generateApiTests = false // No tests please + generateModelTests = false // No tests please + validateSpec = false // No validation please (as in command above) + + generatorName = 'typescript-angular' + inputSpec = oasFile + outputDir = file("${project.projectDir}/frontend/openapi").toString() + configOptions = [ + npmName: '@dres-openapi/api', + ngVersion: '9.1.0', + snapshot: 'true', /// I suggest to remove this, as soon as we automate this, + enumPropertyNaming: 'original' + ] +} + +task generateOAS(type: Download) { + /* Requires DRES running on default port */ + def f = new File(oasFile) + src fullOAS + dest f +} + +task generateClientOAS(type: Download){ + def f = new File(clientOasFile) + src clientOAS + dest f +} \ No newline at end of file diff --git a/frontend/build.gradle b/frontend/build.gradle index d44ba9983..ae3300672 100644 --- a/frontend/build.gradle +++ b/frontend/build.gradle @@ -4,7 +4,7 @@ buildscript { } } plugins { - id 'base' + /// Plugin to handle NodeJS id 'com.github.node-gradle.node' version '3.0.1' } @@ -25,11 +25,10 @@ node { task buildFrontend(type: NpmTask) { args = ['run', 'pbuild'] - dependsOn 'npmInstall' + dependsOn npmInstall + dependsOn rootProject.tasks.getByName('openApiGenerate') } - - artifacts { frontendFiles(file('build/dist')) { builtBy buildFrontend From 3c976414b35da3f6175b7392ed2141d48991acc7 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 9 Mar 2021 16:01:07 +0100 Subject: [PATCH 69/95] Removed circular dependency. --- .../competition-builder-task-dialog.component.ts | 16 +--------------- .../competition-form.builder.ts | 2 +- .../require-match.ts | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 frontend/src/app/competition/competition-builder/competition-builder-task-dialog/require-match.ts diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index ab09307da..b75af1654 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -11,7 +11,7 @@ import { TemporalPoint, TemporalRange } from '../../../../../openapi'; -import {AbstractControl, FormControl, FormGroup} from '@angular/forms'; +import {FormControl, FormGroup} from '@angular/forms'; import {Observable} from 'rxjs'; import {filter, first} from 'rxjs/operators'; import {AppConfig} from '../../../app.config'; @@ -32,20 +32,6 @@ export interface CompetitionBuilderTaskDialogData { task?: RestTaskDescription; } -/** - * https://onthecode.co.uk/force-selection-angular-material-autocomplete/ - * @param control - * @constructor - */ -export function RequireMatch(control: AbstractControl) { - const selection: any = control.value; - if (typeof selection === 'string') { - return {incorrect: true}; - } - return null; -} - - @Component({ selector: 'app-competition-builder-task-dialog', templateUrl: './competition-builder-task-dialog.component.html' diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 244d2cbbd..1a46cb375 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -13,7 +13,7 @@ import { import {FormArray, FormControl, FormGroup, Validators} from '@angular/forms'; import {filter, first, switchMap} from 'rxjs/operators'; import {Observable} from 'rxjs'; -import {RequireMatch} from './competition-builder-task-dialog.component'; +import {RequireMatch} from './require-match'; export class CompetitionFormBuilder { diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/require-match.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/require-match.ts new file mode 100644 index 000000000..3f950ce71 --- /dev/null +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/require-match.ts @@ -0,0 +1,14 @@ +import {AbstractControl} from "@angular/forms"; + +/** + * https://onthecode.co.uk/force-selection-angular-material-autocomplete/ + * @param control + * @constructor + */ +export function RequireMatch(control: AbstractControl) { + const selection: any = control.value; + if (typeof selection === 'string') { + return {incorrect: true}; + } + return null; +} \ No newline at end of file From 68ada987fb21f7f8baa226b79ca03373f9e5174f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 9 Mar 2021 16:02:43 +0100 Subject: [PATCH 70/95] Increased budget for site performance. --- frontend/angular.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/angular.json b/frontend/angular.json index f2939ff00..dc0887a80 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -53,8 +53,8 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" + "maximumWarning": "4mb", + "maximumError": "6mb" }, { "type": "anyComponentStyle", From 5396058f1e0b8c4106f2f1af6c01f50f7fcb401b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 9 Mar 2021 20:25:24 +0100 Subject: [PATCH 71/95] Removed 'task' from JSON serialization of submission to prevent cyclic reference --- .../main/kotlin/dev/dres/data/model/submissions/Submission.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index 04dec6a1d..57e6cd3e8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -23,6 +23,7 @@ import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect sealed class Submission : BaseSubmissionAspect { /** The [AbstractInteractiveTask] this [Submission] belongs to. */ + @JsonIgnore override var task: AbstractInteractiveTask? = null /** The [SubmissionStatus] of this [Submission]. */ From 15acb2fb595a6df974bf4115420fca0fbf06ceac Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 9 Mar 2021 20:55:42 +0100 Subject: [PATCH 72/95] Finalized restructuring of project: - Both ffmpeg and frontend should no be bundled as actual dependencies - Both should be available when running the project as well as when building distributions Fixes #215 --- backend/build.gradle | 42 +++++++------------ backend/src/main/kotlin/dev/dres/DRES.kt | 7 +++- .../kotlin/dev/dres/utilities/FFmpegUtil.kt | 9 ++-- frontend/build.gradle | 22 +++++++++- gradle.properties | 5 +++ 5 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 gradle.properties diff --git a/backend/build.gradle b/backend/build.gradle index 8a94c50ad..2c69b93ea 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -21,7 +21,7 @@ apply plugin: 'idea' mainClassName = 'dev.dres.DRES' sourceCompatibility = 1.8 -/* Configuration for frontent classpath files (see dependencies). */ +/* Configuration for frontend classpath files (see dependencies). */ configurations { frontendClasspath { canBeConsumed = false @@ -29,13 +29,6 @@ configurations { } } -/* Includes the frontendClasspath files into the JAR. */ -jar { - into('html') { - from configurations.frontendClasspath - } -} - repositories { mavenCentral() maven { url "https://kotlin.bintray.com/kotlinx" } @@ -56,7 +49,11 @@ dependencies { def alpnApiVersion = '1.1.3.v20160715' def alpnBootVersion = '8.1.12.v20180117' - frontendClasspath(project(path: ":frontend", configuration: 'frontendFiles')) + ///// Frontend files (produced by sub-project). + implementation frontendClasspath(project(path: ":frontend", configuration: 'frontendFiles')) + + ///// FFMpeg dependency + implementation files("$buildDir/ext") {builtBy 'setupFFMpeg'} ///// MapDB compile group: 'org.mapdb', name: 'mapdb', version: '3.0.8' @@ -139,16 +136,13 @@ sourceSets { distributions { main { distributionBaseName = 'dres-dist' - contents { - from 'ext' - } } } /** Custom tasks: FFmpeg Download and deployment. */ task downloadFFmpeg(type: Download) { - def f = new File('ffmpeg.zip') + def f = new File("$buildDir/build/cache/ffmpeg.zip") outputs.upToDateWhen { return f.exists() } @@ -167,7 +161,7 @@ task downloadFFmpeg(type: Download) { } task downloadFFprobe(type: Download) { - def f = new File('ffprobe.zip') + def f = new File("$buildDir/cache/ffprobe.zip") outputs.upToDateWhen { return f.exists() } @@ -188,21 +182,21 @@ task downloadFFprobe(type: Download) { task copyFFmpeg(type: Copy) { dependsOn downloadFFmpeg outputs.upToDateWhen { - return !fileTree('ext/ffmpeg').filter { it.isFile() && it.name.contains("ffmpeg")}.isEmpty() + return !fileTree("$buildDir/ext/ffmpeg").filter { it.isFile() && it.name== 'ffmpeg'}.isEmpty() } from zipTree(downloadFFmpeg.dest) - into 'ext/ffmpeg' - include '*ffmpeg*' + into "$buildDir/ext/ffmpeg" + include 'ffmpeg' } task copyFFprobe(type: Copy) { dependsOn downloadFFprobe outputs.upToDateWhen { - return !fileTree('ext/ffmpeg').filter { it.isFile() && it.name.contains("ffprobe")}.isEmpty() + return !fileTree("$buildDir/ext/ffmpeg").filter { it.isFile() && it.name == 'ffprobe' }.isEmpty() } from zipTree(downloadFFprobe.dest) - into 'ext/ffmpeg' - include '*ffprobe*' + into "$buildDir/ext/ffmpeg" + include 'ffprobe' } task setupFFMpeg(type: Copy) { @@ -210,10 +204,4 @@ task setupFFMpeg(type: Copy) { dependsOn downloadFFprobe dependsOn copyFFmpeg dependsOn copyFFprobe -} - -/** Distribution tasks depend on Jar and setupFFMpeg. */ -distZip.dependsOn jar -distZip.dependsOn setupFFMpeg -distTar.dependsOn jar -distTar.dependsOn setupFFMpeg \ No newline at end of file +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 8466fede3..d97c0395c 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -18,6 +18,9 @@ import java.nio.file.Paths object DRES { + /** Application root; shoud pe relative to JAR file or classes path. */ + val rootPath = File(FFmpegUtil::class.java.protectionDomain.codeSource.location.toURI()).toPath() + init { //redirect log of JLine3 from jdk logger to log4j System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager") @@ -31,7 +34,9 @@ object DRES { null } ?: Config() - println("initializing...") + println("Starting DRES at $rootPath") + println("Found FFmpeg at ${FFmpegUtil.ffmpegBin}") + println("Initializing...") /* Initialize data access layer. */ val dataAccessLayer = DataAccessLayer(Paths.get(config.dataPath)) diff --git a/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt index 6a7c045dc..311f5925b 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt @@ -7,6 +7,7 @@ import com.github.kokorin.jaffree.ffmpeg.UrlInput import com.github.kokorin.jaffree.ffmpeg.UrlOutput import com.github.kokorin.jaffree.ffprobe.FFprobe import com.github.kokorin.jaffree.ffprobe.FFprobeResult +import dev.dres.DRES import dev.dres.data.model.competition.CachedVideoItem import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory @@ -19,9 +20,9 @@ import java.util.concurrent.Future object FFmpegUtil { /** Path to FFMPEG binary; TODO: Make configurable. */ - private val ffmpegBin = when { - Files.exists(Path.of("ext/ffmpeg/")) -> Path.of("ext/ffmpeg/") /* Runtime */ - Files.exists(Path.of("../ffmpeg/")) -> Path.of("../ffmpeg/") /* Deployment. */ + val ffmpegBin = when { + Files.isDirectory(DRES.rootPath.parent.parent.parent.resolve("ext/ffmpeg")) -> DRES.rootPath.parent.parent.parent.resolve("ext/ffmpeg") + Files.isDirectory(DRES.rootPath.parent.resolve("ffmpeg")) -> DRES.rootPath.parent.resolve("ffmpeg") /* Distribution */ else -> throw IllegalStateException("Could not find valid FFmpeg binary path.") } @@ -39,7 +40,7 @@ object FFmpegUtil { val futureList = mutableListOf>>() - while (threadRunning) { + while (this.threadRunning) { try { futureList.removeIf { diff --git a/frontend/build.gradle b/frontend/build.gradle index ae3300672..26a3d7264 100644 --- a/frontend/build.gradle +++ b/frontend/build.gradle @@ -5,6 +5,7 @@ buildscript { } plugins { /// Plugin to handle NodeJS + id 'base' id 'com.github.node-gradle.node' version '3.0.1' } @@ -24,13 +25,30 @@ node { } task buildFrontend(type: NpmTask) { + outputs.upToDateWhen { + return file("$buildDir/dist").isDirectory() + } args = ['run', 'pbuild'] dependsOn npmInstall dependsOn rootProject.tasks.getByName('openApiGenerate') } +task packageFrontend(type: Zip) { + outputs.upToDateWhen { + return file("$buildDir/lib/dres-frontend.jar").exists() + } + dependsOn buildFrontend + baseName 'dres-frontend' + extension 'jar' + destinationDir file("$buildDir/lib") + from("$buildDir/dist") { + into 'html' + } +} + artifacts { - frontendFiles(file('build/dist')) { - builtBy buildFrontend + frontendFiles(packageFrontend.archiveFile) { + builtBy packageFrontend + type "jar" } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..2278016d6 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +# Project-wide Gradle settings. + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xms64m -Xmx512m \ No newline at end of file From 0e4d0fb3cdc48a255c420e0794113f22b55a68db Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 9 Mar 2021 22:31:53 +0100 Subject: [PATCH 73/95] #215 Added additional, sensible default ffmpeg locations --- backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt index 311f5925b..9b908ab2a 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt @@ -14,6 +14,7 @@ import org.slf4j.MarkerFactory import java.io.File import java.nio.file.Files import java.nio.file.Path +import java.nio.file.Paths import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.Future @@ -23,6 +24,8 @@ object FFmpegUtil { val ffmpegBin = when { Files.isDirectory(DRES.rootPath.parent.parent.parent.resolve("ext/ffmpeg")) -> DRES.rootPath.parent.parent.parent.resolve("ext/ffmpeg") Files.isDirectory(DRES.rootPath.parent.resolve("ffmpeg")) -> DRES.rootPath.parent.resolve("ffmpeg") /* Distribution */ + Files.isDirectory(Paths.get("ext/ffmpeg")) -> Paths.get("ext/ffmpeg") + Files.isDirectory(Paths.get("ffmpeg")) -> Paths.get("ffmpeg") else -> throw IllegalStateException("Could not find valid FFmpeg binary path.") } From 52e15cd901793354d637ea507eabc35a954726d9 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 9 Mar 2021 22:32:40 +0100 Subject: [PATCH 74/95] Fixed #236 by respecting the selected time unit --- README.md | 2 +- ...mpetition-builder-task-dialog.component.ts | 10 +++- frontend/src/app/utilities/time.utilities.ts | 49 +++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 frontend/src/app/utilities/time.utilities.ts diff --git a/README.md b/README.md index 806700999..d4e1ad751 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DRES -[![swagger-editor](https://img.shields.io/badge/open--API-in--editor-brightgreen.svg?style=flat&label=open-api-v3)](https://editor.swagger.io/?url=https://raw.githubusercontent.com/lucaro/DRES/master/doc/openapi-v3.json) +[![swagger-editor](https://img.shields.io/badge/open--API-in--editor-brightgreen.svg?style=flat&label=client%20open-api-v3)](https://editor.swagger.io/?url=https://raw.githubusercontent.com/lucaro/DRES/master/doc/openapi-v3.json) The Distributed Retrieval Evaluation Server builds uppon the work of https://github.com/klschoef/vbsserver/ to provide the means to evaluate interactive retrieval approaches in various settings, both on-site and distributed. diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index b75af1654..4d1dc5a32 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -20,6 +20,8 @@ import { VideoPlayerSegmentBuilderData } from './video-player-segment-builder/video-player-segment-builder.component'; import {AdvancedBuilderDialogComponent, AdvancedBuilderDialogData} from './advanced-builder-dialog/advanced-builder-dialog.component'; +import UnitEnum = TemporalPoint.UnitEnum; +import {TimeUtilities} from '../../../utilities/time.utilities'; /** @@ -180,12 +182,16 @@ export class CompetitionBuilderTaskDialogComponent { */ let start = -1; let end = -1; + const unit = unitControl?.value ? (unitControl.value as UnitEnum) : UnitEnum.SECONDS; if (startControl && startControl.value) { - start = Number.parseInt(startControl.value, 10); + start = TimeUtilities.point2Milliseconds({value: startControl.value, unit} as TemporalPoint, mediaItem.fps) / 1000; + // start = Number.parseInt(startControl.value, 10); } if (endControl && endControl.value) { - end = Number.parseInt(endControl.value, 10); + end = TimeUtilities.point2Milliseconds({value: endControl.value, unit} as TemporalPoint, mediaItem.fps) / 1000; } + + console.log("Start="+start+", End="+end); // const config = { // width: '800px', data: {mediaItem, segmentStart: start, segmentEnd: end} // } as MatDialogConfig; diff --git a/frontend/src/app/utilities/time.utilities.ts b/frontend/src/app/utilities/time.utilities.ts new file mode 100644 index 000000000..ece63a039 --- /dev/null +++ b/frontend/src/app/utilities/time.utilities.ts @@ -0,0 +1,49 @@ +import {TemporalPoint, TemporalRange} from '../../../openapi'; + +/** + * A class with time related utilities. Basically a port of dev.dres.utilities.TimeUtil.kt + */ +export class TimeUtilities { + + + private static msPerHour = 3_600_000; + private static msPerMinute = 60_000; + + static point2Milliseconds(point: TemporalPoint, fps: number): number { + switch (point.unit) { + case 'FRAME_NUMBER': + return (point.value / fps * 1000); + case 'SECONDS': + return (point.value * 1000); + case 'MILLISECONDS': + return point.value; + } + } + + static point2Milliseconds24fps(point: TemporalPoint): number { + return this.point2Milliseconds(point, 24); + } + + static range2Milliseconds(range: TemporalRange, fps: number): [number, number] { + return [this.point2Milliseconds(range.start, fps), this.point2Milliseconds(range.end, fps)]; + } + + static range2Milliseconds24fps(range: TemporalRange): [number, number] { + return this.range2Milliseconds(range, 24); + } + + static timeCode2Milliseconds(timecode: string, fps: number): number { + const matches = timecode.split(':'); + + const hours = matches[0] ? Number.parseInt(matches[0], 10) : 0; + const minutes = matches[1] ? Number.parseInt(matches[1], 10) : 0; + const seconds = matches[2] ? Number.parseInt(matches[2], 10) : 0; + const frames = matches[3] ? Number.parseInt(matches[3], 10) : 0; + + return hours * this.msPerHour + minutes * this.msPerMinute + seconds * 1000 + (1000 * frames / fps); + } + + static timeCode2Milliseconds24fps(timecode: string): number { + return this.timeCode2Milliseconds(timecode, 24); + } +} From fd6bec6ca291696c4722cce1f8c5ba19448fccef Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 9 Mar 2021 22:38:08 +0100 Subject: [PATCH 75/95] Updated JLine dependency. --- backend/build.gradle | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 2c69b93ea..59789f5fb 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -58,9 +58,6 @@ dependencies { ///// MapDB compile group: 'org.mapdb', name: 'mapdb', version: '3.0.8' - ////// JLine 3 - compile group: 'org.jline', name: 'jline', version: '3.15.0' - ////// Javalin compile group: 'io.javalin', name: 'javalin-bundle', version: '3.13.0' compile group: 'io.swagger.core.v3', name: 'swagger-core', version: '2.1.5' @@ -77,11 +74,10 @@ dependencies { ////// bcrypt compile group: 'org.mindrot', name: 'jbcrypt', version: '0.4' - ////// CLIKT - compile group: 'com.github.ajalt', name: 'clikt', version: '2.6.0' - - ///// Picnic - compile group: 'com.jakewharton.picnic', name: 'picnic', version: '0.3.1' + ////// JLine 3, Clikt & Picnic for optimal terminal experience :-) + compile group: 'com.github.ajalt', name: 'clikt', version: '2.8.0' + compile group: 'org.jline', name: 'jline', version: '3.19.0' + compile group: 'com.jakewharton.picnic', name: 'picnic', version: '0.5.0' ///// Fuel compile group: 'com.github.kittinunf.fuel', name: 'fuel', version: '2.3.1' @@ -102,7 +98,6 @@ dependencies { compile group: 'org.apache.logging.log4j', name: 'log4j-jul', version: '2.13.1' ///// JUnit 5 - testCompile "org.jetbrains.kotlin:kotlin-test" testCompile "org.jetbrains.kotlin:kotlin-test-junit5" From 3bcae3443615423a38e54131479bf799301e18e3 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 9 Mar 2021 22:38:40 +0100 Subject: [PATCH 76/95] Cli now explicitly handles UserInterruptException and EndOfFileException --- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 64 +++++++++---------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 78c1b6da3..07a7b3988 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -13,11 +13,11 @@ import org.jline.builtins.Completers import org.jline.reader.* import org.jline.reader.impl.completer.AggregateCompleter import org.jline.reader.impl.completer.StringsCompleter -import org.jline.terminal.Terminal import org.jline.terminal.TerminalBuilder import java.io.IOException import java.util.* import java.util.regex.Pattern +import kotlin.system.exitProcess object Cli { @@ -46,14 +46,11 @@ object Cli { ExecutionCommand() ) - var terminal: Terminal? = null - try { - terminal = TerminalBuilder.terminal() //basic terminal + val terminal = try { + TerminalBuilder.builder().streams(System.`in`, System.out).build() } catch (e: IOException) { - System.err.println("Could not initialize Terminal: ") - System.err.println(e.message) - System.err.println("Exiting...") - System.exit(-1) + System.err.println("Could not initialize terminal: ${e.message}") + exitProcess(-1) } val completer = DelegateCompleter( @@ -88,36 +85,37 @@ object Cli { .build() while (true) { - - val line = lineReader.readLine(PROMPT).trim() - if (line.toLowerCase() == "exit" || line.toLowerCase() == "quit") { - break - } - if (line.toLowerCase() == "help") { - println(clikt.getFormattedHelp()) //TODO overwrite with something more useful in a cli context - continue - } - if (line.isBlank()) { - continue - } - try { - execute(line) - } catch (e: Exception) { - - when (e) { - is com.github.ajalt.clikt.core.NoSuchSubcommand -> println("command not found") - is com.github.ajalt.clikt.core.PrintHelpMessage -> println(e.command.getFormattedHelp()) - is com.github.ajalt.clikt.core.MissingParameter -> println(e.localizedMessage) - is com.github.ajalt.clikt.core.NoSuchOption -> println(e.localizedMessage) - else -> e.printStackTrace() + val line = lineReader.readLine(PROMPT).trim() + if (line.toLowerCase() == "exit" || line.toLowerCase() == "quit") { + break + } + if (line.toLowerCase() == "help") { + println(clikt.getFormattedHelp()) //TODO overwrite with something more useful in a cli context + continue + } + if (line.isBlank()) { + continue } + try { + execute(line) + } catch (e: Exception) { + when (e) { + is com.github.ajalt.clikt.core.NoSuchSubcommand -> println("command not found") + is com.github.ajalt.clikt.core.PrintHelpMessage -> println(e.command.getFormattedHelp()) + is com.github.ajalt.clikt.core.MissingParameter -> println(e.localizedMessage) + is com.github.ajalt.clikt.core.NoSuchOption -> println(e.localizedMessage) + else -> e.printStackTrace() + } + } + } catch (e: EndOfFileException) { + System.err.println("Could not read from terminal due to EOF. If you're running DRES in Docker, try running the container in interactive mode.") + break + } catch (e: UserInterruptException) { + break } - } - - } fun execute(line: String){ From 50345ab9e3b3b2c8bd604ea04e0cc62ec7a24371 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 9 Mar 2021 22:39:55 +0100 Subject: [PATCH 77/95] Added first version of Dockerfile and release.yml. Addresses # --- .github/workflows/release.yml | 66 +++++++++++++++++++++++++++++++++++ Dockerfile | 10 +++--- 2 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..6d2d618a6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,66 @@ +name: Publish DRES Distribution and Docker Image + +on: + push: + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +env: + IMAGE_NAME: dres + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11.0.6 + - name: Cache Gradle packages + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + - name: Build distribution + run: ./gradlew distTar + - name: Get the version + id: get_version + run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.VERSION }} + release_name: Release ${{ env.VERSION }} + draft: false + prerelease: false + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./backend/build/distributions/dres-dist.tar + asset_name: dres-dist-${{ env.VERSION }}.tar + asset_content_type: application/tar + - name: Build Docker Image + run: docker build . --file Dockerfile --tag ${{ env.IMAGE_NAME }} + - name: Log into Docker Registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ secrets.DOCKER_USERNAME }} --password-stdin + - name: Push Docker image + run: | + IMAGE_ID=docker.pkg.github.com/dres-dev/DRES/${{ env.IMAGE_NAME }} + + # Use Docker `latest` tag convention + [ "$VERSION" == "master" ] && VERSION=latest + echo IMAGE_ID=$IMAGE_ID + echo VERSION=$VERSION + docker tag $IMAGE_NAME $IMAGE_ID:$VERSION + docker push $IMAGE_ID:$VERSION \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a9d53948c..4dc567bb3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,19 +2,17 @@ FROM zenika/kotlin:1.4.20-jdk11 AS build COPY . /dres-src RUN cd /dres-src && \ - ./gradlew distTar - - -RUN mkdir dres-dist && \ + ./gradlew distTar && \ + mkdir dres-dist && \ cd dres-dist && \ tar xf ../backend/build/distributions/dres-dist.tar FROM zenika/kotlin:1.4.20-jdk11-slim RUN mkdir /dres-data -COPY backend/config.json /dres-data +COPY config.json /dres-data COPY --from=build /dres-src/dres-dist / EXPOSE 8080 EXPOSE 8443 -ENTRYPOINT /dres-dist/bin/dres /dres-data/config.json \ No newline at end of file +ENTRYPOINT /dres-dist/bin/backend /dres-data/config.json \ No newline at end of file From 4dcf4b2f89a530dc2f9496a40058332663cde3c9 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 9 Mar 2021 22:58:15 +0100 Subject: [PATCH 78/95] docker.pkg.github.com/dres-dev/dres is now all lowercase --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d2d618a6..fb478a21e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ secrets.DOCKER_USERNAME }} --password-stdin - name: Push Docker image run: | - IMAGE_ID=docker.pkg.github.com/dres-dev/DRES/${{ env.IMAGE_NAME }} + IMAGE_ID=docker.pkg.github.com/dres-dev/dres/${{ env.IMAGE_NAME }} # Use Docker `latest` tag convention [ "$VERSION" == "master" ] && VERSION=latest From d27c8b601dfdead97f8a755ce6ce5a67b7ca1a7e Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 9 Mar 2021 23:04:30 +0100 Subject: [PATCH 79/95] #236 - Input type TIMECODE still not respected properly --- ...etition-builder-task-dialog.component.html | 6 +- ...mpetition-builder-task-dialog.component.ts | 65 ++++++++++++------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html index 340c1a468..0d75e33eb 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html @@ -88,17 +88,17 @@

Target

- - - + {{unit}} diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index 4d1dc5a32..3ff657a3b 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -1,7 +1,8 @@ import {Component, ElementRef, Inject, ViewChild} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material/dialog'; import { - CollectionService, ConfiguredOptionQueryComponentOption, + CollectionService, + ConfiguredOptionQueryComponentOption, ConfiguredOptionTargetOption, RestMediaCollection, RestMediaItem, @@ -16,12 +17,10 @@ import {Observable} from 'rxjs'; import {filter, first} from 'rxjs/operators'; import {AppConfig} from '../../../app.config'; import {CompetitionFormBuilder} from './competition-form.builder'; -import { - VideoPlayerSegmentBuilderData -} from './video-player-segment-builder/video-player-segment-builder.component'; +import {VideoPlayerSegmentBuilderData} from './video-player-segment-builder/video-player-segment-builder.component'; import {AdvancedBuilderDialogComponent, AdvancedBuilderDialogData} from './advanced-builder-dialog/advanced-builder-dialog.component'; -import UnitEnum = TemporalPoint.UnitEnum; import {TimeUtilities} from '../../../utilities/time.utilities'; +import UnitEnum = TemporalPoint.UnitEnum; /** @@ -40,17 +39,6 @@ export interface CompetitionBuilderTaskDialogData { }) export class CompetitionBuilderTaskDialogComponent { - constructor(public dialogRef: MatDialogRef, - public collectionService: CollectionService, - @Inject(MAT_DIALOG_DATA) public data: CompetitionBuilderTaskDialogData, - private dialog: MatDialog, - public config: AppConfig) { - - this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, this.data.task); - this.form = this.builder.form; - this.mediaCollectionSource = this.collectionService.getApiCollectionList(); - } - form: FormGroup; units = ['FRAME_NUMBER', 'SECONDS', 'MILLISECONDS', 'TIMECODE']; /** Data source for list of {@link MediaCollection}. Loaded upon construction of the dialog. */ @@ -58,11 +46,24 @@ export class CompetitionBuilderTaskDialogComponent { /** The {@link CompetitionFormBuilder} used by this dialogue. */ builder: CompetitionFormBuilder; @ViewChild('videoPlayer', {static: false}) video: ElementRef; + @ViewChild('targetStartIn', {static: false}) targetStartIn: HTMLInputElement; + @ViewChild('targetEndIn', {static: false}) targetEndIn: HTMLInputElement; viewLayout = 'list'; showVideo = false; videoSegmentData: VideoPlayerSegmentBuilderData; private imagePreviewMap = new Set(); + constructor(public dialogRef: MatDialogRef, + public collectionService: CollectionService, + @Inject(MAT_DIALOG_DATA) public data: CompetitionBuilderTaskDialogData, + private dialog: MatDialog, + public config: AppConfig) { + + this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, this.data.task); + this.form = this.builder.form; + this.mediaCollectionSource = this.collectionService.getApiCollectionList(); + } + private static randInt(min: number, max: number): number { min = Math.floor(min); max = Math.ceil(max); @@ -74,7 +75,7 @@ export class CompetitionBuilderTaskDialogComponent { this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, task); this.form = this.builder.form; console.log('Loaded task: ' + JSON.stringify(task)); - } + }; /** * Handler for (+) button for query target form component. @@ -184,14 +185,22 @@ export class CompetitionBuilderTaskDialogComponent { let end = -1; const unit = unitControl?.value ? (unitControl.value as UnitEnum) : UnitEnum.SECONDS; if (startControl && startControl.value) { - start = TimeUtilities.point2Milliseconds({value: startControl.value, unit} as TemporalPoint, mediaItem.fps) / 1000; + if (startControl.value === 'TIMECODE') { + start = TimeUtilities.timeCode2Milliseconds(startControl.value, mediaItem.fps) / 1000; + } else { + start = TimeUtilities.point2Milliseconds({value: startControl.value, unit} as TemporalPoint, mediaItem.fps) / 1000; + } // start = Number.parseInt(startControl.value, 10); } if (endControl && endControl.value) { - end = TimeUtilities.point2Milliseconds({value: endControl.value, unit} as TemporalPoint, mediaItem.fps) / 1000; + if (endControl.value === 'TIMECODE') { + end = TimeUtilities.timeCode2Milliseconds(endControl.value, mediaItem.fps) / 1000; + } else { + end = TimeUtilities.point2Milliseconds({value: endControl.value, unit} as TemporalPoint, mediaItem.fps) / 1000; + } } - console.log("Start="+start+", End="+end); + console.log('Start=' + start + ', End=' + end); // const config = { // width: '800px', data: {mediaItem, segmentStart: start, segmentEnd: end} // } as MatDialogConfig; @@ -208,7 +217,7 @@ export class CompetitionBuilderTaskDialogComponent { this.showVideo = !this.showVideo; } - onRangeChange( range: TemporalRange, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl){ + onRangeChange(range: TemporalRange, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { startControl?.setValue(range.start.value); endControl?.setValue(range.end.value); unitControl?.setValue(TemporalPoint.UnitEnum.SECONDS); @@ -270,11 +279,23 @@ export class CompetitionBuilderTaskDialogComponent { const nameNoExt = name.substring(0, name.lastIndexOf('.')); this.collectionService.getApiCollectionWithCollectionidWithStartswith(mediaCollectionId, nameNoExt) .subscribe(item => - form.get('mediaItem').setValue(item[0])); + form.get('mediaItem').setValue(item[0])); }); }); } + targetTimeUnitChanged(unitControl: FormControl) { + const type = unitControl.value === 'TIMECODE' ? 'text' : 'number'; + console.log("new type: "+type); + if (this.targetStartIn) { + this.targetStartIn.type = type; + } + if (this.targetEndIn) { + this.targetEndIn.type = type; + } + console.log(this.targetStartIn); + } + /** * Handler for 'close' button. */ From 2d967d07f439246b84dbc42c6ec2359172f12bfe Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 10 Mar 2021 08:35:46 +0100 Subject: [PATCH 80/95] Fixed wrong path in build.gradle --- backend/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/build.gradle b/backend/build.gradle index 59789f5fb..c4c81e53f 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -137,7 +137,7 @@ distributions { /** Custom tasks: FFmpeg Download and deployment. */ task downloadFFmpeg(type: Download) { - def f = new File("$buildDir/build/cache/ffmpeg.zip") + def f = new File("$buildDir/cache/ffmpeg.zip") outputs.upToDateWhen { return f.exists() } From 9755d67212549eb17a9fc48e4034d9b5d06e4f9e Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 10 Mar 2021 08:53:44 +0100 Subject: [PATCH 81/95] #215 Fixed ffmpeg for windows / multi platform --- backend/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index c4c81e53f..83a5c8085 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -177,21 +177,21 @@ task downloadFFprobe(type: Download) { task copyFFmpeg(type: Copy) { dependsOn downloadFFmpeg outputs.upToDateWhen { - return !fileTree("$buildDir/ext/ffmpeg").filter { it.isFile() && it.name== 'ffmpeg'}.isEmpty() + return !fileTree("$buildDir/ext/ffmpeg").filter { it.isFile() && it.name.startsWith('ffmpeg')}.isEmpty() } from zipTree(downloadFFmpeg.dest) into "$buildDir/ext/ffmpeg" - include 'ffmpeg' + include '*ffmpeg*' } task copyFFprobe(type: Copy) { dependsOn downloadFFprobe outputs.upToDateWhen { - return !fileTree("$buildDir/ext/ffmpeg").filter { it.isFile() && it.name == 'ffprobe' }.isEmpty() + return !fileTree("$buildDir/ext/ffmpeg").filter { it.isFile() && it.name.startsWith('ffprobe') }.isEmpty() } from zipTree(downloadFFprobe.dest) into "$buildDir/ext/ffmpeg" - include 'ffprobe' + include '*ffprobe*' } task setupFFMpeg(type: Copy) { From a020f37e4825fd678627a7f60d6c96b20d9956b7 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 10 Mar 2021 11:07:52 +0100 Subject: [PATCH 82/95] Fixed issue that would prevent ending a run when a task just ended. --- .../kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 162590faa..2d547bf39 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -186,7 +186,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun end(context: RunActionContext) { - checkGlobalStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE) + checkGlobalStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE, RunManagerStatus.TASK_ENDED) if (context.isAdmin) { /* End the run. */ this.run.end() From 9bc44c25681394b01773e27feeb577fbe4079e87 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 10 Mar 2021 11:08:29 +0100 Subject: [PATCH 83/95] And again! --- .../kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 437d7027d..10c715272 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -181,7 +181,7 @@ class InteractiveSynchronousRunManager(val run: InteractiveSynchronousCompetitio } override fun end(context: RunActionContext) = this.stateLock.write { - checkStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE) + checkStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE, RunManagerStatus.TASK_ENDED) checkContext(context) /* End the run. */ From 7aea07608dc4558a5fd1cedd1ad872df83830860 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 10 Mar 2021 11:25:34 +0100 Subject: [PATCH 84/95] Dirty flag is now set when moving tasks. Fixes bug introduced in #220 --- .../competition-builder/competition-builder.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index 37eae29c4..bfd499411 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -347,6 +347,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat this.competition.tasks[oldIndex - 1] = task; this.competition.tasks[oldIndex] = buffer; this.taskTable.renderRows(); + this.dirty = true; } } @@ -362,6 +363,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat this.competition.tasks[oldIndex + 1] = task; this.competition.tasks[oldIndex] = buffer; this.taskTable.renderRows(); + this.dirty = true; } } From c29a799ccc0bbb746079560b63670aef3f396616 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 10 Mar 2021 11:58:21 +0100 Subject: [PATCH 85/95] Fixed vote counting logic in BasicVoteValidator --- .../dev/dres/run/validation/judged/BasicVoteValidator.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index 21f86c0e2..4ce941ba3 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -60,7 +60,8 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() val sum = voteCountMap.values.sum() if (sum < minimumVotes) return false val max = voteCountMap.values.maxOrNull() ?: 0 - return (sum - max) <= voteDifference + val others = sum - max + return max - others >= voteDifference } override fun nextSubmissionToVoteOn(): Submission? = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? From 362f0a73c05a4a21703fd2e426cfafce2163bbdc Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 10 Mar 2021 11:58:55 +0100 Subject: [PATCH 86/95] Config now ignores unknown values during parsing --- backend/src/main/kotlin/dev/dres/data/model/Config.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/Config.kt b/backend/src/main/kotlin/dev/dres/data/model/Config.kt index dd85fabd5..0589b975c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/Config.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/Config.kt @@ -1,9 +1,10 @@ package dev.dres.data.model - +import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.databind.ObjectMapper import java.io.File +@JsonIgnoreProperties(ignoreUnknown = true) data class Config( val httpPort: Int = 8080, val httpsPort: Int = 8443, From 29caf6a0f2c8601c3823ac537bd229f3df4024b7 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 12 Mar 2021 11:34:18 +0100 Subject: [PATCH 87/95] Fixed #236 By properly checking time unit --- ...etition-builder-task-dialog.component.html | 20 ++--- ...mpetition-builder-task-dialog.component.ts | 18 ++--- .../competition-form.builder.ts | 74 ++++++++++--------- 3 files changed, 59 insertions(+), 53 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html index 0d75e33eb..e33748bc9 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html @@ -88,17 +88,17 @@

Target

- - - + {{unit}} @@ -170,17 +170,17 @@

Target

- - - + {{unit}} @@ -306,17 +306,17 @@

Query description - - - + + + diff --git a/frontend/src/app/run/admin-run-list.component.ts b/frontend/src/app/run/admin-run-list.component.ts index 49c5ad3b4..151a7619e 100644 --- a/frontend/src/app/run/admin-run-list.component.ts +++ b/frontend/src/app/run/admin-run-list.component.ts @@ -1,6 +1,6 @@ import {Component} from '@angular/core'; import {AbstractRunListComponent} from './abstract-run-list.component'; -import {CompetitionRunAdminService, CompetitionRunService} from '../../../openapi'; +import {CompetitionRunAdminService, CompetitionRunScoresService, CompetitionRunService} from '../../../openapi'; import {MatSnackBar} from '@angular/material/snack-bar'; import {Router} from '@angular/router'; @@ -11,9 +11,10 @@ import {Router} from '@angular/router'; export class AdminRunListComponent extends AbstractRunListComponent{ constructor(runService: CompetitionRunService, runAdminService: CompetitionRunAdminService, + scoreService: CompetitionRunScoresService, router: Router, private snackBar: MatSnackBar) { - super(runService, runAdminService, router); + super(runService, runAdminService, scoreService, router); } public start(runId: string) { diff --git a/frontend/src/app/run/viewer-run-list.component.html b/frontend/src/app/run/viewer-run-list.component.html index 2d436be8d..81821a0f3 100644 --- a/frontend/src/app/run/viewer-run-list.component.html +++ b/frontend/src/app/run/viewer-run-list.component.html @@ -19,6 +19,10 @@ gavel + + diff --git a/frontend/src/app/run/viewer-run-list.component.ts b/frontend/src/app/run/viewer-run-list.component.ts index 016282413..03ae4be8f 100644 --- a/frontend/src/app/run/viewer-run-list.component.ts +++ b/frontend/src/app/run/viewer-run-list.component.ts @@ -1,6 +1,6 @@ import {Component} from '@angular/core'; import {AbstractRunListComponent, RunInfoWithState} from './abstract-run-list.component'; -import {CompetitionRunAdminService, CompetitionRunService} from '../../../openapi'; +import {CompetitionRunAdminService, CompetitionRunScoresService, CompetitionRunService} from '../../../openapi'; import {Router} from '@angular/router'; import {AccessChecking} from '../model/access-checking.interface'; import {UserGroup} from '../model/user-group.model'; @@ -18,9 +18,10 @@ export class ViewerRunListComponent extends AbstractRunListComponent implements constructor(runService: CompetitionRunService, runAdminService: CompetitionRunAdminService, + scoreService: CompetitionRunScoresService, router: Router, private accessService: AccessRoleService) { - super(runService, runAdminService, router); + super(runService, runAdminService, scoreService, router); } hasAccessFor(group: UserGroup): boolean { From c6bfed136b6167ab68de54ead1e4b4584aced730 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 12 Mar 2021 14:46:42 +0100 Subject: [PATCH 90/95] Minor adjustements and code beautification --- .../competition-form.builder.ts | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 5aaa82676..a4fab32b0 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -57,6 +57,17 @@ export class CompetitionFormBuilder { }; } + private temporalPointValidator(unitControl: FormControl): ValidatorFn{ + return (control: AbstractControl): { [key: string]: any } | null => { + if (unitControl.value === 'TIMECODE'){ + // TODO properly check format HH:MM:SS:fff + return `${control.value}`.length >= 5 && `${control.value}`.indexOf(':') > 0 ? null : {error: `${control.value} does not conform HH:MM:SS:fff format`}; + }else{ + return Validators.min(0); + } + }; + } + /** * Returns the {@link Observable} for the given key. * @@ -266,13 +277,20 @@ export class CompetitionFormBuilder { }); } - return new FormGroup({ + const formGroup = new FormGroup({ mediaItem: mediaItemFormControl, - segment_start: new FormControl(initialize?.temporalRange.start.value, [Validators.required, this.orValidator(Validators.min(0), Validators.minLength(5))]), - segment_end: new FormControl(initialize?.temporalRange.end.value, [Validators.required, this.orValidator(Validators.min(0), Validators.minLength(5))]), + segment_start: new FormControl(initialize?.temporalRange.start.value, [Validators.required]), + segment_end: new FormControl(initialize?.temporalRange.end.value, [Validators.required]), segment_time_unit: new FormControl(initialize?.temporalRange.start.unit ? initialize?.temporalRange.start.unit : 'SECONDS', [Validators.required]) }); + + formGroup.get('segment_start').setValidators([Validators.required, this.temporalPointValidator(formGroup.get('segment_time_unit') as FormControl)]); + formGroup.get('segment_end').setValidators([Validators.required, this.temporalPointValidator(formGroup.get('segment_time_unit') as FormControl)]); + formGroup.get('segment_start').updateValueAndValidity(); + formGroup.get('segment_end').updateValueAndValidity(); + + return formGroup; } /** @@ -372,8 +390,8 @@ export class CompetitionFormBuilder { end: new FormControl(initialize?.end), type: new FormControl('VIDEO_ITEM_SEGMENT', [Validators.required]), mediaItem: mediaItemFormControl, - segment_start: new FormControl(initialize?.range.start.value, [Validators.required, this.orValidator(Validators.min(0), Validators.minLength(5))]), - segment_end: new FormControl(initialize?.range.end.value, [Validators.required, this.orValidator(Validators.min(0), Validators.minLength(5))]), + segment_start: new FormControl(initialize?.range.start.value, [Validators.required]), + segment_end: new FormControl(initialize?.range.end.value, [Validators.required]), segment_time_unit: new FormControl(initialize?.range.start.unit ? initialize?.range.start.unit : 'SECONDS', Validators.required) }); @@ -391,6 +409,11 @@ export class CompetitionFormBuilder { group.get('segment_time_unit').setValue((this.form.get('target') as FormArray).controls[0].get('segment_time_unit').value); } + group.get('segment_start').setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as FormControl)]); + group.get('segment_end').setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as FormControl)]); + group.get('segment_start').updateValueAndValidity(); + group.get('segment_end').updateValueAndValidity(); + return group; } From 7bb91fa75095358ea4dd5414301cacface03fced Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 12 Mar 2021 15:05:38 +0100 Subject: [PATCH 91/95] Enabled pretty print for OAS. This will make version control easier --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 5 + doc/oas-client.json | 889 ++- doc/oas.json | 5205 ++++++++++++++++- 3 files changed, 6097 insertions(+), 2 deletions(-) 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 cad6654e4..c3ceca2fd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest +import com.fasterxml.jackson.databind.SerializationFeature import dev.dres.api.rest.handler.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.dbo.DataAccessLayer @@ -10,6 +11,7 @@ import io.javalin.apibuilder.ApiBuilder.* import io.javalin.core.security.SecurityUtil.roles import io.javalin.plugin.openapi.OpenApiOptions import io.javalin.plugin.openapi.OpenApiPlugin +import io.javalin.plugin.openapi.jackson.JacksonToJsonMapper import io.javalin.plugin.openapi.ui.ReDocOptions import io.javalin.plugin.openapi.ui.SwaggerOptions import io.swagger.v3.oas.models.info.Info @@ -34,6 +36,8 @@ object RestApi { private val logMarker = MarkerFactory.getMarker("REST") private val logger = LoggerFactory.getLogger(this.javaClass) + private val mapper = JacksonToJsonMapper.defaultObjectMapper.enable(SerializationFeature.INDENT_OUTPUT) + fun getOpenApiPlugin(): OpenApiPlugin { return openApiPlugin } @@ -247,6 +251,7 @@ object RestApi { } activateAnnotationScanningFor("dev.dres.api.rest.handler") options.ignored.forEach { ignorePath(it.first) } + toJsonMapper(JacksonToJsonMapper(jacksonMapper)) } diff --git a/doc/oas-client.json b/doc/oas-client.json index 75751d52a..68bc3a8c6 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1 +1,888 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessfulSubmissionsStatus"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessfulSubmissionsStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"412":{"description":"Precondition Failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"SuccessfulSubmissionsStatus":{"required":["description","status","submission"],"type":"object","properties":{"submission":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"description":{"type":"string"},"status":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{ + "openapi" : "3.0.1", + "info" : { + "title" : "DRES API", + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", + "version" : "1.0" + }, + "paths" : { + "/api/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiLogin", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiLogout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/user/{userId}" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id", + "operationId" : "getApiUserWithUserid", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiUserWithUserid", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", + "operationId" : "patchApiUserWithUserid", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiUserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SessionId" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiStatusTime", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + } + } + }, + "/api/runInfo/list" : { + "get" : { + "tags" : [ "Client Run Info" ], + "summary" : "Lists an overview of all competition runs visible to the current client", + "operationId" : "getApiRuninfoList", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ClientRunInfoList" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/runInfo/currentTask/{runId}" : { + "get" : { + "tags" : [ "Client Run Info" ], + "summary" : "Returns an overview of the currently active task for a run", + "operationId" : "getApiRuninfoCurrenttaskWithRunid", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ClientTaskInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getSubmit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g. video).", + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g. video).", + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g. video).", + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postLogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants", + "operationId" : "postLogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "required" : [ "password", "username" ], + "type" : "object", + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + } + }, + "UserDetails" : { + "required" : [ "id", "role", "username" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "type" : "string", + "enum" : [ "ADMIN", "JUDGE", "VIEWER", "PARTICIPANT" ] + }, + "sessionId" : { + "type" : "string" + } + } + }, + "ErrorStatus" : { + "required" : [ "description", "status" ], + "type" : "object", + "properties" : { + "description" : { + "type" : "string" + }, + "status" : { + "type" : "boolean" + } + } + }, + "SuccessStatus" : { + "required" : [ "description", "status" ], + "type" : "object", + "properties" : { + "description" : { + "type" : "string" + }, + "status" : { + "type" : "boolean" + } + } + }, + "UserRequest" : { + "required" : [ "username" ], + "type" : "object", + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "type" : "string", + "enum" : [ "ADMIN", "JUDGE", "VIEWER", "PARTICIPANT" ] + } + } + }, + "SessionId" : { + "required" : [ "sessionId" ], + "type" : "object", + "properties" : { + "sessionId" : { + "type" : "string" + } + } + }, + "CurrentTime" : { + "required" : [ "timeStamp" ], + "type" : "object", + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "ClientRunInfo" : { + "required" : [ "id", "name", "status" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "status" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "PREPARING_TASK", "RUNNING_TASK", "TASK_ENDED", "TERMINATED" ] + } + } + }, + "ClientRunInfoList" : { + "required" : [ "runs" ], + "type" : "object", + "properties" : { + "runs" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ClientRunInfo" + } + } + } + }, + "ClientTaskInfo" : { + "required" : [ "id", "name", "remainingTime", "running", "taskGroup" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "remainingTime" : { + "type" : "integer", + "format" : "int64" + }, + "running" : { + "type" : "boolean" + } + } + }, + "SuccessfulSubmissionsStatus" : { + "required" : [ "description", "status", "submission" ], + "type" : "object", + "properties" : { + "submission" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "description" : { + "type" : "string" + }, + "status" : { + "type" : "boolean" + } + } + }, + "QueryEvent" : { + "required" : [ "category", "timestamp", "type", "value" ], + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + } + }, + "QueryEventLog" : { + "required" : [ "events", "timestamp" ], + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + } + } + }, + "QueryResult" : { + "required" : [ "item" ], + "type" : "object", + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + } + }, + "QueryResultLog" : { + "required" : [ "events", "resultSetAvailability", "results", "sortType", "timestamp" ], + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index 6bd2d1756..0655ba04a 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1 +1,5204 @@ -{"openapi":"3.0.1","info":{"title":"DRES API","description":"API for DRES (Distributed Retrieval Evaluation Server), Version 1.0","version":"1.0"},"paths":{"/api/login":{"post":{"tags":["User"],"summary":"Sets roles for session based on user account and returns a session cookie.","operationId":"postApiLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/logout":{"get":{"tags":["User"],"summary":"Clears all user roles of the current session.","operationId":"getApiLogout","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/list":{"get":{"tags":["User"],"summary":"Lists all available users.","operationId":"getApiUserList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}}}}},"/api/user":{"get":{"tags":["User"],"summary":"Get information about the current user.","operationId":"getApiUser","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"post":{"tags":["User"],"summary":"Creates a new user, if the username is not already taken. Requires ADMIN privileges","operationId":"postApiUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"If the username is already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/{userId}":{"get":{"tags":["User"],"summary":"Gets details of the user with the given id","operationId":"getApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User's UID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["User"],"summary":"Deletes the specified user. Requires ADMIN privileges","operationId":"deleteApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"404":{"description":"If the user could not be found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["User"],"summary":"Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone","operationId":"patchApiUserWithUserid","parameters":[{"name":"userId","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session":{"get":{"tags":["User"],"summary":"Get current sessionId","operationId":"getApiUserSession","parameters":[{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionId"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/user/session/active/list":{"get":{"tags":["User"],"summary":"Get details of all current user sessions","operationId":"getApiUserSessionActiveList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/list":{"get":{"tags":["Collection"],"summary":"Lists all available media collections with basic information about their content.","operationId":"getApiCollectionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaCollection"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}":{"get":{"tags":["Collection"],"summary":"Shows the content of the specified media collection.","operationId":"getApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestFullMediaCollection"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Deletes a media collection","operationId":"deleteApiCollectionWithCollectionid","parameters":[{"name":"collectionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection":{"post":{"tags":["Collection"],"summary":"Adds a new media collection","operationId":"postApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a media collection","operationId":"patchApiCollection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaCollection"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem":{"post":{"tags":["Collection"],"summary":"Adds a Media Item to the specified Media Collection.","operationId":"postApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Collection"],"summary":"Updates a Media Item to the specified Media Collection.","operationId":"patchApiMediaitem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/mediaItem/{mediaId}":{"get":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"getApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Collection"],"summary":"Selects and returns a specific media item.","operationId":"deleteApiMediaitemWithMediaid","parameters":[{"name":"mediaId","in":"path","description":"Media item ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/random":{"get":{"tags":["Collection"],"summary":"Selects and returns a random media item from a given media collection.","operationId":"getApiCollectionWithCollectionidRandom","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestMediaItem"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/collection/{collectionId}/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists media items from a given media collection whose name start with the given string.","operationId":"getApiCollectionWithCollectionidWithStartswith","parameters":[{"name":"collectionId","in":"path","description":"Collection ID","required":true,"schema":{"type":"string"}},{"name":"startsWith","in":"path","description":"Name starts with","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/external/{startsWith}":{"get":{"tags":["Collection"],"summary":"Lists items from the external media collection whose name start with the given string.","operationId":"getApiExternalWithStartswith","parameters":[{"name":"startsWith","in":"path","description":"Name starts with.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/list":{"get":{"tags":["Competition"],"summary":"Lists an overview of all available competitions with basic information about their content.","operationId":"getApiCompetitionList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition":{"post":{"tags":["Competition"],"summary":"Creates a new competition.","operationId":"postApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"patch":{"tags":["Competition"],"summary":"Updates an existing competition.","operationId":"patchApiCompetition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}":{"get":{"tags":["Competition"],"summary":"Loads the detailed definition of a specific competition.","operationId":"getApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestCompetitionDescription"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}},"delete":{"tags":["Competition"],"summary":"Deletes the competition with the given competition ID.","operationId":"deleteApiCompetitionWithCompetitionid","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list":{"get":{"tags":["Competition"],"summary":"Lists the Teams of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTeamList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/team/list/details":{"get":{"tags":["Competition"],"summary":"Lists the teams with their user details","operationId":"getApiCompetitionWithCompetitionidTeamListDetails","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestDetailedTeam"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/competition/{competitionId}/task/list":{"get":{"tags":["Competition"],"summary":"Lists the Tasks of a specific competition.","operationId":"getApiCompetitionWithCompetitionidTaskList","parameters":[{"name":"competitionId","in":"path","description":"Competition ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunInfoList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/list":{"get":{"tags":["Competition Run"],"summary":"Lists an overview of all competition runs visible to the current user","operationId":"getApiRunStateList","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunState"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/info/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns a specific competition run.","operationId":"getApiRunInfoWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/state/{runId}":{"get":{"tags":["Competition Run"],"summary":"Returns the state of a specific competition run.","operationId":"getApiRunStateWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunState"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/hint":{"get":{"tags":["Competition Run"],"summary":"Returns the task hint for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidHint","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskHint"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/target":{"get":{"tags":["Competition Run"],"summary":"Returns the task target for the current task run (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTarget","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskTarget"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task":{"get":{"tags":["Competition Run"],"summary":"Returns the information for the current task (i.e. the one that is currently selected).","operationId":"getApiRunWithRunidTask","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task run, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorStatus"},{"$ref":"#/components/schemas/ErrorStatus"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/submissions/after/{timestamp}":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.","operationId":"getApiRunWithRunidSubmissionsAfterWithTimestamp","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"timestamp","in":"path","description":"Minimum Timestamp of returned submissions.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/task/{taskId}/submissions":{"get":{"tags":["Competition Run"],"summary":"Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.","operationId":"getApiRunWithRunidTaskWithTaskidSubmissions","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the score overviews of a specific competition run.","operationId":"getApiScoreRunWithRunid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreOverview"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/current":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the current task run, if it is either running or has just ended.","operationId":"getApiScoreRunWithRunidCurrent","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/history/{taskId}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns the overviews of all score boards for the specified task run.","operationId":"getApiScoreRunWithRunidHistoryWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreOverview"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/series/{scoreboard}":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a time series for a given run and scoreboard.","operationId":"getApiScoreRunWithRunidSeriesWithScoreboard","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}},{"name":"scoreboard","in":"path","description":"Name of the scoreboard to return the time series for.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeries"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/scoreboards":{"get":{"tags":["Competition Run Scores"],"summary":"Returns a list of available scoreboard names for the given run.","operationId":"getApiScoreRunWithRunidScoreboards","parameters":[{"name":"runId","in":"path","description":"ID of the competition run.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/score/run/{runId}/tasks/csv":{"get":{"tags":["Competition Run Scores"],"summary":"Provides a CSV with the scores for a given competition run","operationId":"getApiScoreRunWithRunidTasksCsv","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/create":{"post":{"tags":["Competition Run Admin"],"summary":"Creates a new competition run from an existing competition","operationId":"postApiRunAdminCreate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionStartMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/next":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the next task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskNext","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/previous":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the previous task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskPrevious","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/switch/{idx}":{"post":{"tags":["Competition Run Admin"],"summary":"Moves to and selects the specified task. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskSwitchWithIdx","parameters":[{"name":"runId","in":"path","description":"Competition run ID","required":true,"schema":{"type":"string"}},{"name":"idx","in":"path","description":"Index of the task to switch to.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/start":{"post":{"tags":["Competition Run Admin"],"summary":"Starts the currently active task as a new task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskStart","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/task/abort":{"post":{"tags":["Competition Run Admin"],"summary":"Aborts the currently running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTaskAbort","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/terminate":{"post":{"tags":["Competition Run Admin"],"summary":"Terminates a competition run. This is a method for admins.","operationId":"postApiRunAdminWithRunidTerminate","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/adjust/{duration}":{"post":{"tags":["Competition Run Admin"],"summary":"Adjusts the duration of a running task run. This is a method for admins.","operationId":"postApiRunAdminWithRunidAdjustWithDuration","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"duration","in":"path","description":"Duration to add.","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewer/list":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all registered viewers for a competition run. This is a method for admins.","operationId":"getApiRunAdminWithRunidViewerList","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ViewerInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/viewers/{viewerId}/force":{"post":{"tags":["Competition Run Admin"],"summary":"Forces a viewer with the given viewer ID into the READY state. This is a method for admins.","operationId":"postApiRunAdminWithRunidViewersWithVieweridForce","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"viewerId","in":"path","description":"Viewer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/list/{taskId}":{"get":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"getApiRunAdminWithRunidSubmissionsListWithTaskid","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","description":"Task ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionInfo"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/admin/{runId}/submissions/override":{"patch":{"tags":["Competition Run Admin"],"summary":"Lists all submissions for a given task and run","operationId":"patchApiRunAdminWithRunidSubmissionsOverride","parameters":[{"name":"runId","in":"path","description":"Competition Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionInfo"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to be judged.","operationId":"getApiRunWithRunidJudgeNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/vote/next":{"get":{"tags":["Judgement"],"summary":"Gets the next open Submission to voted on.","operationId":"getApiRunWithRunidVoteNext","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementRequest"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge":{"post":{"tags":["Judgement"],"summary":"Returns a Judgement.","operationId":"postApiRunWithRunidJudge","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Judgement"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/status":{"get":{"tags":["Judgement"],"summary":"Gets the status of all judgement validators.","operationId":"getApiRunWithRunidJudgeStatus","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JudgementValidatorStatus"}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/run/{runId}/judge/vote":{"post":{"tags":["Judgement"],"summary":"Returns a Vote.","operationId":"postApiRunWithRunidJudgeVote","parameters":[{"name":"runId","in":"path","description":"Run ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JudgementVote"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/info":{"get":{"tags":["Audit"],"summary":"Gives information about the audit log. Namely size and latest timestamp of known audit logs","operationId":"getApiAuditInfo","responses":{"200":{"description":"The audit log info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogInfo"}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/logs/{since}/{upto}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditLogsWithSinceWithUpto","parameters":[{"name":"since","in":"path","description":"Timestamp of the earliest audit log to include","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"upto","in":"path","description":"Timestamp of the latest audit log to include.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/audit/list/{limit}/{page}":{"get":{"tags":["Audit"],"summary":"Lists all audit logs matching the query","operationId":"getApiAuditListWithLimitWithPage","parameters":[{"name":"limit","in":"path","description":"The maximum number of results. Default: 500","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"page","in":"path","description":"The page index offset, relative to the limit","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"The audit logs","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RestAuditLogEntry"}}}}},"403":{"description":"Whenever a non-admin user starts the call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/status/time":{"get":{"tags":["Status"],"summary":"Returns the current time on the server.","operationId":"getApiStatusTime","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentTime"}}}}}}},"/api/runInfo/list":{"get":{"tags":["Client Run Info"],"summary":"Lists an overview of all competition runs visible to the current client","operationId":"getApiRuninfoList","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRunInfoList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/api/runInfo/currentTask/{runId}":{"get":{"tags":["Client Run Info"],"summary":"Returns an overview of the currently active task for a run","operationId":"getApiRuninfoCurrenttaskWithRunid","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientTaskInfo"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/submit":{"get":{"tags":["Submission"],"summary":"Endpoint to accept submissions","operationId":"getSubmit","parameters":[{"name":"collection","in":"query","description":"Collection identifier. Optional, in which case the default collection for the run will be considered.","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"item","in":"query","description":"Identifier for the actual media object or media file.","schema":{"type":"string"}},{"name":"frame","in":"query","description":"Frame number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"shot","in":"query","description":"Shot number for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"integer","format":"int32"}},{"name":"timecode","in":"query","description":"Timecode for media with temporal progression (e.g. video).","allowEmptyValue":true,"schema":{"type":"string"}},{"name":"session","in":"query","description":"Session Token","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessfulSubmissionsStatus"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessfulSubmissionsStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"412":{"description":"Precondition Failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/query":{"post":{"tags":["Log"],"summary":"Accepts query logs from participants","operationId":"postLogQuery","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryEventLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}},"/log/result":{"post":{"tags":["Log"],"summary":"Accepts result logs from participants","operationId":"postLogResult","parameters":[{"name":"session","in":"query","description":"Session Token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResultLog"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessStatus"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorStatus"}}}}}}}},"components":{"schemas":{"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"UserDetails":{"required":["id","role","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]},"sessionId":{"type":"string"}}},"ErrorStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"SuccessStatus":{"required":["description","status"],"type":"object","properties":{"description":{"type":"string"},"status":{"type":"boolean"}}},"UserRequest":{"required":["username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["ADMIN","JUDGE","VIEWER","PARTICIPANT"]}}},"SessionId":{"required":["sessionId"],"type":"object","properties":{"sessionId":{"type":"string"}}},"RestMediaCollection":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"basePath":{"type":"string"}}},"RestFullMediaCollection":{"required":["collection","items"],"type":"object","properties":{"collection":{"$ref":"#/components/schemas/RestMediaCollection"},"items":{"type":"array","items":{"$ref":"#/components/schemas/RestMediaItem"}}}},"RestMediaItem":{"required":["collectionId","id","location","name","type"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["IMAGE","VIDEO"]},"collectionId":{"type":"string"},"location":{"type":"string"},"durationMs":{"type":"integer","format":"int64"},"fps":{"type":"number","format":"float"}}},"CompetitionOverview":{"required":["description","id","name","taskCount","teamCount"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskCount":{"type":"integer","format":"int32"},"teamCount":{"type":"integer","format":"int32"}}},"CompetitionCreate":{"required":["description","name","participantsCanView"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"ConfiguredOptionQueryComponentOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionScoringOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["KIS","AVS"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSimpleOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["HIDDEN_RESULTS","MAP_TO_SEGMENT","PROLONG_ON_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionSubmissionFilterOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["NO_DUPLICATES","LIMIT_CORRECT_PER_TEAM","LIMIT_WRONG_PER_TEAM","LIMIT_TOTAL_PER_TEAM","LIMIT_CORRECT_PER_MEMBER","TEMPORAL_SUBMISSION"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"ConfiguredOptionTargetOption":{"required":["option","parameters"],"type":"object","properties":{"option":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}}}},"RestCompetitionDescription":{"required":["id","name","participantCanView","taskGroups","taskTypes","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"taskTypes":{"type":"array","items":{"$ref":"#/components/schemas/TaskType"}},"taskGroups":{"type":"array","items":{"$ref":"#/components/schemas/TaskGroup"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescription"}},"teams":{"type":"array","items":{"$ref":"#/components/schemas/RestTeam"}},"participantCanView":{"type":"boolean"}}},"RestTaskDescription":{"required":["components","duration","id","mediaCollectionId","name","target","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"},"mediaCollectionId":{"type":"string"},"target":{"$ref":"#/components/schemas/RestTaskDescriptionTarget"},"components":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionComponent"}}}},"RestTaskDescriptionComponent":{"required":["type"],"type":"object","properties":{"type":{"type":"string","enum":["IMAGE_ITEM","VIDEO_ITEM_SEGMENT","TEXT","EXTERNAL_IMAGE","EXTERNAL_VIDEO"]},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"description":{"type":"string"},"path":{"type":"string"},"dataType":{"type":"string"},"mediaItem":{"type":"string"},"range":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTaskDescriptionTarget":{"required":["mediaItems","type"],"type":"object","properties":{"type":{"type":"string","enum":["SINGLE_MEDIA_ITEM","SINGLE_MEDIA_SEGMENT","MULTIPLE_MEDIA_ITEMS","JUDGEMENT","VOTE"]},"mediaItems":{"type":"array","items":{"$ref":"#/components/schemas/RestTaskDescriptionTargetItem"}}}},"RestTaskDescriptionTargetItem":{"required":["mediaItem"],"type":"object","properties":{"mediaItem":{"type":"string"},"temporalRange":{"$ref":"#/components/schemas/TemporalRange"}}},"RestTeam":{"required":["color","name","users"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoData":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"TaskGroup":{"required":["name","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}}},"TaskType":{"required":["components","filter","name","options","score","targetType","taskDuration"],"type":"object","properties":{"name":{"type":"string"},"taskDuration":{"type":"integer","format":"int64"},"targetType":{"$ref":"#/components/schemas/ConfiguredOptionTargetOption"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionQueryComponentOption"}},"score":{"$ref":"#/components/schemas/ConfiguredOptionScoringOption"},"filter":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSubmissionFilterOption"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/ConfiguredOptionSimpleOption"}}}},"TemporalPoint":{"required":["unit","value"],"type":"object","properties":{"value":{"type":"number","format":"double"},"unit":{"type":"string","enum":["FRAME_NUMBER","SECONDS","MILLISECONDS"]}}},"TemporalRange":{"required":["end","start"],"type":"object","properties":{"start":{"$ref":"#/components/schemas/TemporalPoint"},"end":{"$ref":"#/components/schemas/TemporalPoint"}}},"RestDetailedTeam":{"required":["color","logoId","name","users"],"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserDetails"}}}},"RunInfo":{"required":["competitionId","id","name","participantsCanView","tasks","teams"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamInfo"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskInfo"}},"competitionId":{"type":"string"},"participantsCanView":{"type":"boolean"}}},"TaskInfo":{"required":["duration","id","name","taskGroup","taskType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"taskType":{"type":"string"},"duration":{"type":"integer","format":"int64"}}},"TeamInfo":{"required":["color","logoId","name","uid"],"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"logoId":{"type":"string"}}},"RunState":{"required":["id","status","timeLeft"],"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]},"currentTask":{"$ref":"#/components/schemas/TaskInfo"},"timeLeft":{"type":"integer","format":"int64"}}},"ContentElement":{"required":["contentType","offset"],"type":"object","properties":{"contentType":{"type":"string","enum":["EMPTY","TEXT","VIDEO","IMAGE"]},"content":{"type":"string"},"offset":{"type":"integer","format":"int64"}}},"TaskHint":{"required":["loop","sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}},"loop":{"type":"boolean"}}},"TaskTarget":{"required":["sequence","taskId"],"type":"object","properties":{"taskId":{"type":"string"},"sequence":{"type":"array","items":{"$ref":"#/components/schemas/ContentElement"}}}},"SubmissionInfo":{"required":["member","status","team","timestamp"],"type":"object","properties":{"id":{"type":"string"},"team":{"type":"string"},"member":{"type":"string"},"status":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"timestamp":{"type":"integer","format":"int64"},"item":{"$ref":"#/components/schemas/RestMediaItem"},"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"}}},"Score":{"required":["score","teamId"],"type":"object","properties":{"teamId":{"type":"string"},"score":{"type":"number","format":"double"}}},"ScoreOverview":{"required":["name","scores"],"type":"object","properties":{"name":{"type":"string"},"taskGroup":{"type":"string"},"scores":{"type":"array","items":{"$ref":"#/components/schemas/Score"}}}},"ScoreSeries":{"required":["name","points","team"],"type":"object","properties":{"team":{"type":"string"},"name":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ScoreSeriesPoint"}}}},"ScoreSeriesPoint":{"required":["score","timestamp"],"type":"object","properties":{"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"}}},"CompetitionStartMessage":{"required":["competitionId","name","scoreboards","type"],"type":"object","properties":{"competitionId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SYNCHRONOUS","ASYNCHRONOUS"]},"scoreboards":{"type":"array","items":{"type":"string"}}}},"ViewerInfo":{"required":["host","ready","username","viewersId"],"type":"object","properties":{"viewersId":{"type":"string"},"username":{"type":"string"},"host":{"type":"string"},"ready":{"type":"boolean"}}},"JudgementRequest":{"required":["collection","item","taskDescription","token","validator"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"collection":{"type":"string"},"item":{"type":"string"},"taskDescription":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"}}},"Judgement":{"required":["token","validator","verdict"],"type":"object","properties":{"token":{"type":"string"},"validator":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"JudgementValidatorStatus":{"required":["open","pending","validator"],"type":"object","properties":{"validator":{"type":"string"},"pending":{"type":"integer","format":"int32"},"open":{"type":"integer","format":"int32"}}},"JudgementVote":{"required":["verdict"],"type":"object","properties":{"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]}}},"AuditLogInfo":{"required":["latest","size","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int32"},"latest":{"type":"integer","format":"int64"}}},"RestAuditLogEntry":{"required":["id","timestamp","type"],"type":"object","properties":{"type":{"type":"string","enum":["COMPETITION_START","COMPETITION_END","TASK_START","TASK_MODIFIED","TASK_END","SUBMISSION","PREPARE_JUDGEMENT","JUDGEMENT","LOGIN","LOGOUT"]},"id":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}},"RestCompetitionEndAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestCompetitionStartAuditLogEntry":{"required":["api","competition","id","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestJudgementAuditLogEntry":{"required":["api","competition","id","timestamp","token","type","validator","verdict"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"validator":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestLoginAuditLogEntry":{"required":["api","id","session","timestamp","type","user"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"user":{"type":"string"},"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestLogoutAuditLogEntry":{"required":["api","id","session","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"session":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]}}}]},"RestPrepareJudgementAuditLogEntry":{"required":["id","submission","timestamp","token","type","validator"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"validator":{"type":"string"},"token":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"}}}]},"RestSubmissionAuditLogEntry":{"required":["address","api","competition","id","submission","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"submission":{"$ref":"#/components/schemas/SubmissionInfo"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"},"address":{"type":"string"}}}]},"RestTaskEndAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskModifiedAuditLogEntry":{"required":["api","competition","id","modification","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"modification":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"RestTaskStartAuditLogEntry":{"required":["api","competition","id","taskName","timestamp","type"],"type":"object","allOf":[{"$ref":"#/components/schemas/RestAuditLogEntry"},{"type":"object","properties":{"competition":{"type":"string"},"taskName":{"type":"string"},"api":{"type":"string","enum":["REST","CLI","INTERNAL"]},"user":{"type":"string"}}}]},"CurrentTime":{"required":["timeStamp"],"type":"object","properties":{"timeStamp":{"type":"integer","format":"int64"}}},"ClientRunInfo":{"required":["id","name","status"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["CREATED","ACTIVE","PREPARING_TASK","RUNNING_TASK","TASK_ENDED","TERMINATED"]}}},"ClientRunInfoList":{"required":["runs"],"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/ClientRunInfo"}}}},"ClientTaskInfo":{"required":["id","name","remainingTime","running","taskGroup"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskGroup":{"type":"string"},"remainingTime":{"type":"integer","format":"int64"},"running":{"type":"boolean"}}},"SuccessfulSubmissionsStatus":{"required":["description","status","submission"],"type":"object","properties":{"submission":{"type":"string","enum":["CORRECT","WRONG","INDETERMINATE","UNDECIDABLE"]},"description":{"type":"string"},"status":{"type":"boolean"}}},"QueryEvent":{"required":["category","timestamp","type","value"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"category":{"type":"string","enum":["TEXT","IMAGE","SKETCH","FILTER","BROWSING","COOPERATION","OTHER"]},"type":{"type":"string"},"value":{"type":"string"}}},"QueryEventLog":{"required":["events","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}},"QueryResult":{"required":["item"],"type":"object","properties":{"item":{"type":"string"},"segment":{"type":"integer","format":"int32"},"frame":{"type":"integer","format":"int32"},"score":{"type":"number","format":"double"},"rank":{"type":"integer","format":"int32"}}},"QueryResultLog":{"required":["events","resultSetAvailability","results","sortType","timestamp"],"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"sortType":{"type":"string"},"resultSetAvailability":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/QueryResult"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/QueryEvent"}}}}}}} \ No newline at end of file +{ + "openapi" : "3.0.1", + "info" : { + "title" : "DRES API", + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", + "version" : "1.0" + }, + "paths" : { + "/api/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiLogin", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiLogout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "operationId" : "getApiUserList", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + } + } + } + } + }, + "/api/user" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiUser", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiUser", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/user/{userId}" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id", + "operationId" : "getApiUserWithUserid", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiUserWithUserid", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", + "operationId" : "patchApiUserWithUserid", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiUserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SessionId" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "operationId" : "getApiUserSessionActiveList", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", + "operationId" : "getApiCollectionList", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestMediaCollection" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/collection/{collectionId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiCollectionWithCollectionid", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestFullMediaCollection" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes a media collection", + "operationId" : "deleteApiCollectionWithCollectionid", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/collection" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a new media collection", + "operationId" : "postApiCollection", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaCollection" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a media collection", + "operationId" : "patchApiCollection", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaCollection" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a Media Item to the specified Media Collection.", + "operationId" : "postApiMediaitem", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiMediaitem", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/mediaItem/{mediaId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "getApiMediaitemWithMediaid", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "deleteApiMediaitemWithMediaid", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/collection/{collectionId}/random" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", + "operationId" : "getApiCollectionWithCollectionidRandom", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/collection/{collectionId}/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", + "operationId" : "getApiCollectionWithCollectionidWithStartswith", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/external/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "operationId" : "getApiExternalWithStartswith", + "parameters" : [ { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with.", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/competition/list" : { + "get" : { + "tags" : [ "Competition" ], + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "operationId" : "getApiCompetitionList", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/CompetitionOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/competition" : { + "post" : { + "tags" : [ "Competition" ], + "summary" : "Creates a new competition.", + "operationId" : "postApiCompetition", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CompetitionCreate" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "patch" : { + "tags" : [ "Competition" ], + "summary" : "Updates an existing competition.", + "operationId" : "patchApiCompetition", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestCompetitionDescription" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/competition/{competitionId}" : { + "get" : { + "tags" : [ "Competition" ], + "summary" : "Loads the detailed definition of a specific competition.", + "operationId" : "getApiCompetitionWithCompetitionid", + "parameters" : [ { + "name" : "competitionId", + "in" : "path", + "description" : "Competition ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestCompetitionDescription" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + }, + "delete" : { + "tags" : [ "Competition" ], + "summary" : "Deletes the competition with the given competition ID.", + "operationId" : "deleteApiCompetitionWithCompetitionid", + "parameters" : [ { + "name" : "competitionId", + "in" : "path", + "description" : "Competition ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/competition/{competitionId}/team/list" : { + "get" : { + "tags" : [ "Competition" ], + "summary" : "Lists the Teams of a specific competition.", + "operationId" : "getApiCompetitionWithCompetitionidTeamList", + "parameters" : [ { + "name" : "competitionId", + "in" : "path", + "description" : "Competition ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/competition/{competitionId}/team/list/details" : { + "get" : { + "tags" : [ "Competition" ], + "summary" : "Lists the teams with their user details", + "operationId" : "getApiCompetitionWithCompetitionidTeamListDetails", + "parameters" : [ { + "name" : "competitionId", + "in" : "path", + "description" : "Competition ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestDetailedTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/competition/{competitionId}/task/list" : { + "get" : { + "tags" : [ "Competition" ], + "summary" : "Lists the Tasks of a specific competition.", + "operationId" : "getApiCompetitionWithCompetitionidTaskList", + "parameters" : [ { + "name" : "competitionId", + "in" : "path", + "description" : "Competition ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTaskDescription" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/info/list" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Lists an overview of all competition runs visible to the current user", + "operationId" : "getApiRunInfoList", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RunInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/state/list" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Lists an overview of all competition runs visible to the current user", + "operationId" : "getApiRunStateList", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RunState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/info/{runId}" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns a specific competition run.", + "operationId" : "getApiRunInfoWithRunid", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/state/{runId}" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the state of a specific competition run.", + "operationId" : "getApiRunStateWithRunid", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/hint" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the task hint for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiRunWithRunidHint", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TaskHint" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/target" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiRunWithRunidTarget", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TaskTarget" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/task" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the information for the current task (i.e. the one that is currently selected).", + "operationId" : "getApiRunWithRunidTask", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TaskInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/submissions" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the submissions for the current task run, if it is either running or has just ended.", + "operationId" : "getApiRunWithRunidSubmissions", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "oneOf" : [ { + "$ref" : "#/components/schemas/ErrorStatus" + }, { + "$ref" : "#/components/schemas/ErrorStatus" + } ] + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/submissions/after/{timestamp}" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.", + "operationId" : "getApiRunWithRunidSubmissionsAfterWithTimestamp", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Minimum Timestamp of returned submissions.", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/task/{taskId}/submissions" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiRunWithRunidTaskWithTaskidSubmissions", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/score/run/{runId}" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns the score overviews of a specific competition run.", + "operationId" : "getApiScoreRunWithRunid", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ScoreOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/score/run/{runId}/current" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns the overviews of all score boards for the current task run, if it is either running or has just ended.", + "operationId" : "getApiScoreRunWithRunidCurrent", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/score/run/{runId}/history/{taskId}" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task run.", + "operationId" : "getApiScoreRunWithRunidHistoryWithTaskid", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition run ID", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/score/run/{runId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", + "operationId" : "getApiScoreRunWithRunidSeriesWithScoreboard", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "ID of the competition run.", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ScoreSeries" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/score/run/{runId}/scoreboards" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns a list of available scoreboard names for the given run.", + "operationId" : "getApiScoreRunWithRunidScoreboards", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "ID of the competition run.", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/score/run/{runId}/tasks/csv" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Provides a CSV with the scores for a given competition run", + "operationId" : "getApiScoreRunWithRunidTasksCsv", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/csv" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/create" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Creates a new competition run from an existing competition", + "operationId" : "postApiRunAdminCreate", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CompetitionStartMessage" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/start" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Starts a competition run. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidStart", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/task/next" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Moves to and selects the next task. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidTaskNext", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/task/previous" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidTaskPrevious", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidTaskSwitchWithIdx", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition run ID", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/task/start" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidTaskStart", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/task/abort" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidTaskAbort", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/terminate" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Terminates a competition run. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidTerminate", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/adjust/{duration}" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Adjusts the duration of a running task run. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidAdjustWithDuration", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add.", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/viewer/list" : { + "get" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Lists all registered viewers for a competition run. This is a method for admins.", + "operationId" : "getApiRunAdminWithRunidViewerList", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/viewers/{viewerId}/force" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiRunAdminWithRunidViewersWithVieweridForce", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "Viewer ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/submissions/list/{taskId}" : { + "get" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Lists all submissions for a given task and run", + "operationId" : "getApiRunAdminWithRunidSubmissionsListWithTaskid", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/admin/{runId}/submissions/override" : { + "patch" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Lists all submissions for a given task and run", + "operationId" : "patchApiRunAdminWithRunidSubmissionsOverride", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open Submission to be judged.", + "operationId" : "getApiRunWithRunidJudgeNext", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/JudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open Submission to voted on.", + "operationId" : "getApiRunWithRunidVoteNext", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/JudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Judgement.", + "operationId" : "postApiRunWithRunidJudge", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Judgement" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the status of all judgement validators.", + "operationId" : "getApiRunWithRunidJudgeStatus", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/JudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/run/{runId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiRunWithRunidJudgeVote", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Run ID", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/JudgementVote" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/audit/info" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Gives information about the audit log. Namely size and latest timestamp of known audit logs", + "operationId" : "getApiAuditInfo", + "responses" : { + "200" : { + "description" : "The audit log info", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AuditLogInfo" + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/audit/logs/{since}/{upto}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query", + "operationId" : "getApiAuditLogsWithSinceWithUpto", + "parameters" : [ { + "name" : "since", + "in" : "path", + "description" : "Timestamp of the earliest audit log to include", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "upto", + "in" : "path", + "description" : "Timestamp of the latest audit log to include.", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "The audit logs", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/audit/list/{limit}/{page}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query", + "operationId" : "getApiAuditListWithLimitWithPage", + "parameters" : [ { + "name" : "limit", + "in" : "path", + "description" : "The maximum number of results. Default: 500", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "page", + "in" : "path", + "description" : "The page index offset, relative to the limit", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "The audit logs", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiStatusTime", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + } + } + }, + "/api/runInfo/list" : { + "get" : { + "tags" : [ "Client Run Info" ], + "summary" : "Lists an overview of all competition runs visible to the current client", + "operationId" : "getApiRuninfoList", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ClientRunInfoList" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/api/runInfo/currentTask/{runId}" : { + "get" : { + "tags" : [ "Client Run Info" ], + "summary" : "Returns an overview of the currently active task for a run", + "operationId" : "getApiRuninfoCurrenttaskWithRunid", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ClientTaskInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getSubmit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g. video).", + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g. video).", + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g. video).", + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postLogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + }, + "/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants", + "operationId" : "postLogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + } + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "required" : [ "password", "username" ], + "type" : "object", + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + } + }, + "UserDetails" : { + "required" : [ "id", "role", "username" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "type" : "string", + "enum" : [ "ADMIN", "JUDGE", "VIEWER", "PARTICIPANT" ] + }, + "sessionId" : { + "type" : "string" + } + } + }, + "ErrorStatus" : { + "required" : [ "description", "status" ], + "type" : "object", + "properties" : { + "description" : { + "type" : "string" + }, + "status" : { + "type" : "boolean" + } + } + }, + "SuccessStatus" : { + "required" : [ "description", "status" ], + "type" : "object", + "properties" : { + "description" : { + "type" : "string" + }, + "status" : { + "type" : "boolean" + } + } + }, + "UserRequest" : { + "required" : [ "username" ], + "type" : "object", + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "type" : "string", + "enum" : [ "ADMIN", "JUDGE", "VIEWER", "PARTICIPANT" ] + } + } + }, + "SessionId" : { + "required" : [ "sessionId" ], + "type" : "object", + "properties" : { + "sessionId" : { + "type" : "string" + } + } + }, + "RestMediaCollection" : { + "required" : [ "id", "name" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + } + } + }, + "RestFullMediaCollection" : { + "required" : [ "collection", "items" ], + "type" : "object", + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/RestMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + } + }, + "RestMediaItem" : { + "required" : [ "collectionId", "id", "location", "name", "type" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO" ] + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + } + } + }, + "CompetitionOverview" : { + "required" : [ "description", "id", "name", "taskCount", "teamCount" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskCount" : { + "type" : "integer", + "format" : "int32" + }, + "teamCount" : { + "type" : "integer", + "format" : "int32" + } + } + }, + "CompetitionCreate" : { + "required" : [ "description", "name", "participantsCanView" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "participantsCanView" : { + "type" : "boolean" + } + } + }, + "ConfiguredOptionQueryComponentOption" : { + "required" : [ "option", "parameters" ], + "type" : "object", + "properties" : { + "option" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "parameters" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } + }, + "ConfiguredOptionScoringOption" : { + "required" : [ "option", "parameters" ], + "type" : "object", + "properties" : { + "option" : { + "type" : "string", + "enum" : [ "KIS", "AVS" ] + }, + "parameters" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } + }, + "ConfiguredOptionSimpleOption" : { + "required" : [ "option", "parameters" ], + "type" : "object", + "properties" : { + "option" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "parameters" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } + }, + "ConfiguredOptionSubmissionFilterOption" : { + "required" : [ "option", "parameters" ], + "type" : "object", + "properties" : { + "option" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION" ] + }, + "parameters" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } + }, + "ConfiguredOptionTargetOption" : { + "required" : [ "option", "parameters" ], + "type" : "object", + "properties" : { + "option" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "MULTIPLE_MEDIA_ITEMS", "JUDGEMENT", "VOTE" ] + }, + "parameters" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } + }, + "RestCompetitionDescription" : { + "required" : [ "id", "name", "participantCanView", "taskGroups", "taskTypes", "tasks", "teams" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTaskDescription" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTeam" + } + }, + "participantCanView" : { + "type" : "boolean" + } + } + }, + "RestTaskDescription" : { + "required" : [ "components", "duration", "id", "mediaCollectionId", "name", "target", "taskGroup", "taskType" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "mediaCollectionId" : { + "type" : "string" + }, + "target" : { + "$ref" : "#/components/schemas/RestTaskDescriptionTarget" + }, + "components" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTaskDescriptionComponent" + } + } + } + }, + "RestTaskDescriptionComponent" : { + "required" : [ "type" ], + "type" : "object", + "properties" : { + "type" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/TemporalRange" + } + } + }, + "RestTaskDescriptionTarget" : { + "required" : [ "mediaItems", "type" ], + "type" : "object", + "properties" : { + "type" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "MULTIPLE_MEDIA_ITEMS", "JUDGEMENT", "VOTE" ] + }, + "mediaItems" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTaskDescriptionTargetItem" + } + } + } + }, + "RestTaskDescriptionTargetItem" : { + "required" : [ "mediaItem" ], + "type" : "object", + "properties" : { + "mediaItem" : { + "type" : "string" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + } + }, + "RestTeam" : { + "required" : [ "color", "name", "users" ], + "type" : "object", + "properties" : { + "uid" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "logoData" : { + "type" : "string" + }, + "logoId" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "TaskGroup" : { + "required" : [ "name", "type" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + } + }, + "TaskType" : { + "required" : [ "components", "filter", "name", "options", "score", "targetType", "taskDuration" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "taskDuration" : { + "type" : "integer", + "format" : "int64" + }, + "targetType" : { + "$ref" : "#/components/schemas/ConfiguredOptionTargetOption" + }, + "components" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ConfiguredOptionQueryComponentOption" + } + }, + "score" : { + "$ref" : "#/components/schemas/ConfiguredOptionScoringOption" + }, + "filter" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ConfiguredOptionSubmissionFilterOption" + } + }, + "options" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ConfiguredOptionSimpleOption" + } + } + } + }, + "TemporalPoint" : { + "required" : [ "unit", "value" ], + "type" : "object", + "properties" : { + "value" : { + "type" : "number", + "format" : "double" + }, + "unit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS" ] + } + } + }, + "TemporalRange" : { + "required" : [ "end", "start" ], + "type" : "object", + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + } + } + }, + "RestDetailedTeam" : { + "required" : [ "color", "logoId", "name", "users" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "logoId" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + } + }, + "RunInfo" : { + "required" : [ "competitionId", "id", "name", "participantsCanView", "tasks", "teams" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TeamInfo" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskInfo" + } + }, + "competitionId" : { + "type" : "string" + }, + "participantsCanView" : { + "type" : "boolean" + } + } + }, + "TaskInfo" : { + "required" : [ "duration", "id", "name", "taskGroup", "taskType" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "TeamInfo" : { + "required" : [ "color", "logoId", "name", "uid" ], + "type" : "object", + "properties" : { + "uid" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "logoId" : { + "type" : "string" + } + } + }, + "RunState" : { + "required" : [ "id", "status", "timeLeft" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "status" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "PREPARING_TASK", "RUNNING_TASK", "TASK_ENDED", "TERMINATED" ] + }, + "currentTask" : { + "$ref" : "#/components/schemas/TaskInfo" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "ContentElement" : { + "required" : [ "contentType", "offset" ], + "type" : "object", + "properties" : { + "contentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "TaskHint" : { + "required" : [ "loop", "sequence", "taskId" ], + "type" : "object", + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + } + }, + "TaskTarget" : { + "required" : [ "sequence", "taskId" ], + "type" : "object", + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ContentElement" + } + } + } + }, + "SubmissionInfo" : { + "required" : [ "member", "status", "team", "timestamp" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "team" : { + "type" : "string" + }, + "member" : { + "type" : "string" + }, + "status" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "item" : { + "$ref" : "#/components/schemas/RestMediaItem" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "Score" : { + "required" : [ "score", "teamId" ], + "type" : "object", + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + } + }, + "ScoreOverview" : { + "required" : [ "name", "scores" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Score" + } + } + } + }, + "ScoreSeries" : { + "required" : [ "name", "points", "team" ], + "type" : "object", + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ScoreSeriesPoint" + } + } + } + }, + "ScoreSeriesPoint" : { + "required" : [ "score", "timestamp" ], + "type" : "object", + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "CompetitionStartMessage" : { + "required" : [ "competitionId", "name", "scoreboards", "type" ], + "type" : "object", + "properties" : { + "competitionId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS" ] + }, + "scoreboards" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "ViewerInfo" : { + "required" : [ "host", "ready", "username", "viewersId" ], + "type" : "object", + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + } + }, + "JudgementRequest" : { + "required" : [ "collection", "item", "taskDescription", "token", "validator" ], + "type" : "object", + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "collection" : { + "type" : "string" + }, + "item" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "startTime" : { + "type" : "string" + }, + "endTime" : { + "type" : "string" + } + } + }, + "Judgement" : { + "required" : [ "token", "validator", "verdict" ], + "type" : "object", + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + } + } + }, + "JudgementValidatorStatus" : { + "required" : [ "open", "pending", "validator" ], + "type" : "object", + "properties" : { + "validator" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + } + }, + "JudgementVote" : { + "required" : [ "verdict" ], + "type" : "object", + "properties" : { + "verdict" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + } + } + }, + "AuditLogInfo" : { + "required" : [ "latest", "size", "timestamp" ], + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "RestAuditLogEntry" : { + "required" : [ "id", "timestamp", "type" ], + "type" : "object", + "properties" : { + "type" : { + "type" : "string", + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT" ] + }, + "id" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "RestCompetitionEndAuditLogEntry" : { + "required" : [ "api", "competition", "id", "timestamp", "type" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "competition" : { + "type" : "string" + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "user" : { + "type" : "string" + } + } + } ] + }, + "RestCompetitionStartAuditLogEntry" : { + "required" : [ "api", "competition", "id", "timestamp", "type" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "competition" : { + "type" : "string" + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "user" : { + "type" : "string" + } + } + } ] + }, + "RestJudgementAuditLogEntry" : { + "required" : [ "api", "competition", "id", "timestamp", "token", "type", "validator", "verdict" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "competition" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "token" : { + "type" : "string" + }, + "verdict" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "user" : { + "type" : "string" + } + } + } ] + }, + "RestLoginAuditLogEntry" : { + "required" : [ "api", "id", "session", "timestamp", "type", "user" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "user" : { + "type" : "string" + }, + "session" : { + "type" : "string" + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + } + } + } ] + }, + "RestLogoutAuditLogEntry" : { + "required" : [ "api", "id", "session", "timestamp", "type" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "session" : { + "type" : "string" + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + } + } + } ] + }, + "RestPrepareJudgementAuditLogEntry" : { + "required" : [ "id", "submission", "timestamp", "token", "type", "validator" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "validator" : { + "type" : "string" + }, + "token" : { + "type" : "string" + }, + "submission" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } + } + } ] + }, + "RestSubmissionAuditLogEntry" : { + "required" : [ "address", "api", "competition", "id", "submission", "taskName", "timestamp", "type" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "competition" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "submission" : { + "$ref" : "#/components/schemas/SubmissionInfo" + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "user" : { + "type" : "string" + }, + "address" : { + "type" : "string" + } + } + } ] + }, + "RestTaskEndAuditLogEntry" : { + "required" : [ "api", "competition", "id", "taskName", "timestamp", "type" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "competition" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "user" : { + "type" : "string" + } + } + } ] + }, + "RestTaskModifiedAuditLogEntry" : { + "required" : [ "api", "competition", "id", "modification", "taskName", "timestamp", "type" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "competition" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "modification" : { + "type" : "string" + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "user" : { + "type" : "string" + } + } + } ] + }, + "RestTaskStartAuditLogEntry" : { + "required" : [ "api", "competition", "id", "taskName", "timestamp", "type" ], + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/RestAuditLogEntry" + }, { + "type" : "object", + "properties" : { + "competition" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "api" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "user" : { + "type" : "string" + } + } + } ] + }, + "CurrentTime" : { + "required" : [ "timeStamp" ], + "type" : "object", + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "ClientRunInfo" : { + "required" : [ "id", "name", "status" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "status" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "PREPARING_TASK", "RUNNING_TASK", "TASK_ENDED", "TERMINATED" ] + } + } + }, + "ClientRunInfoList" : { + "required" : [ "runs" ], + "type" : "object", + "properties" : { + "runs" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ClientRunInfo" + } + } + } + }, + "ClientTaskInfo" : { + "required" : [ "id", "name", "remainingTime", "running", "taskGroup" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "remainingTime" : { + "type" : "integer", + "format" : "int64" + }, + "running" : { + "type" : "boolean" + } + } + }, + "SuccessfulSubmissionsStatus" : { + "required" : [ "description", "status", "submission" ], + "type" : "object", + "properties" : { + "submission" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "description" : { + "type" : "string" + }, + "status" : { + "type" : "boolean" + } + } + }, + "QueryEvent" : { + "required" : [ "category", "timestamp", "type", "value" ], + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + } + }, + "QueryEventLog" : { + "required" : [ "events", "timestamp" ], + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + } + } + }, + "QueryResult" : { + "required" : [ "item" ], + "type" : "object", + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + } + }, + "QueryResultLog" : { + "required" : [ "events", "resultSetAvailability", "results", "sortType", "timestamp" ], + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + } + } + } + } + } +} \ No newline at end of file From aa6dc0e928508d64a0b3c9207533d8963dc3c49e Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 15 Mar 2021 12:54:37 +0100 Subject: [PATCH 92/95] Fixed #243 By adjusting deployment process. --- frontend/README.md | 27 +++++++++++++++++++++++++++ frontend/build.gradle | 9 ++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/frontend/README.md b/frontend/README.md index fe2b7ce57..0902c176d 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -4,6 +4,33 @@ This is the frontend / UI code for DRES - the Distributed Retrieval Evaluation S This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 9.1.0. +## Configuration Customisation + +It is possible to customise the configuration __before__ deoploying. +The main reason for that is enabling TLS. + +Please modify the `src/config.json` accordingly and package +the frontend using the corresponding gradle command: + +``` +./gradlew packageFrontend -DincludeConfig +``` + +The config is structured as follows: + +```json +{ + "endpoint": { + "host": "localhost", // When serving the frontend elsewhere, specify the backend host + "port": 8080, // When serving the frontend elsewhere, specify the backend port + "tls": false // Set to `true` when TLS should be enabled. Requires backend config with matching `"enableSsl":true` + }, + "effects": { + "mute": false // Whether to globally mute audio (fx and video) + } +} +``` + ## Development server Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. diff --git a/frontend/build.gradle b/frontend/build.gradle index 26a3d7264..888c9ef78 100644 --- a/frontend/build.gradle +++ b/frontend/build.gradle @@ -16,6 +16,9 @@ configurations { } } +/** If the flag -DincludeConfig is passed, we include it */ +def includeConfig = project.hasProperty('includeConfig') + node { version = '12.18.3' download = true @@ -42,6 +45,10 @@ task packageFrontend(type: Zip) { extension 'jar' destinationDir file("$buildDir/lib") from("$buildDir/dist") { + println("includeConfig: "+includeConfig) + if(!includeConfig){ + exclude '**/config.json' + } into 'html' } } @@ -51,4 +58,4 @@ artifacts { builtBy packageFrontend type "jar" } -} \ No newline at end of file +} From b4fd3324b227d4ca212703381df16e4536070087 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 20 Mar 2021 14:40:27 +0100 Subject: [PATCH 93/95] Updated OpenAPI badge, closes #238 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4e1ad751..246f9e971 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DRES -[![swagger-editor](https://img.shields.io/badge/open--API-in--editor-brightgreen.svg?style=flat&label=client%20open-api-v3)](https://editor.swagger.io/?url=https://raw.githubusercontent.com/lucaro/DRES/master/doc/openapi-v3.json) +[![swagger-editor](https://img.shields.io/badge/open--API-in--editor-brightgreen.svg?style=flat&label=client%20open-api-v3)](https://editor.swagger.io/?url=https://raw.githubusercontent.com/dres-dev/DRES/dev/doc/oas-client.json) The Distributed Retrieval Evaluation Server builds uppon the work of https://github.com/klschoef/vbsserver/ to provide the means to evaluate interactive retrieval approaches in various settings, both on-site and distributed. From f51cac39887bfc28f0c397746230dd05c89bb76d Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 23 Mar 2021 19:25:19 +0100 Subject: [PATCH 94/95] Have OpenAPI button point to master branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 246f9e971..b94fbaef5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DRES -[![swagger-editor](https://img.shields.io/badge/open--API-in--editor-brightgreen.svg?style=flat&label=client%20open-api-v3)](https://editor.swagger.io/?url=https://raw.githubusercontent.com/dres-dev/DRES/dev/doc/oas-client.json) +[![swagger-editor](https://img.shields.io/badge/open--API-in--editor-brightgreen.svg?style=flat&label=client%20open-api-v3)](https://editor.swagger.io/?url=https://raw.githubusercontent.com/dres-dev/DRES/master/doc/oas-client.json) The Distributed Retrieval Evaluation Server builds uppon the work of https://github.com/klschoef/vbsserver/ to provide the means to evaluate interactive retrieval approaches in various settings, both on-site and distributed. From de6fcb2c7a7dc480067eb943bee055436b803020 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 23 Mar 2021 23:16:25 +0100 Subject: [PATCH 95/95] Excluding logback-dep of javalin: No more multiple slf4j bindings --- backend/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/build.gradle b/backend/build.gradle index 83a5c8085..6eea066ad 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -59,7 +59,9 @@ dependencies { compile group: 'org.mapdb', name: 'mapdb', version: '3.0.8' ////// Javalin - compile group: 'io.javalin', name: 'javalin-bundle', version: '3.13.0' + compile ('io.javalin:javalin-bundle:3.13.0'){ + exclude group: 'ch.qos.logback', module: 'logback-classic' + } compile group: 'io.swagger.core.v3', name: 'swagger-core', version: '2.1.5' compile group: 'org.webjars', name: 'swagger-ui', version: '3.24.3' compile group: 'org.webjars.npm', name: 'js-tokens', version: '3.0.2'