├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── codeql-analysis.yml │ ├── issue-closed-labeler.yml │ └── release.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── backend ├── README.md ├── build.gradle ├── config │ └── log4j2.xml ├── keystore.jks └── src │ ├── main │ ├── kotlin │ │ └── dev │ │ │ └── dres │ │ │ ├── DRES.kt │ │ │ ├── api │ │ │ ├── cli │ │ │ │ ├── Cli.kt │ │ │ │ ├── ConfigCommand.kt │ │ │ │ ├── EvaluationCommand.kt │ │ │ │ ├── EvaluationTemplateCommand.kt │ │ │ │ ├── ExecutionCommand.kt │ │ │ │ ├── MediaCollectionCommand.kt │ │ │ │ ├── OpenApiCommand.kt │ │ │ │ └── UserCommand.kt │ │ │ └── rest │ │ │ │ ├── AccessManager.kt │ │ │ │ ├── ClientOpenApiPlugin.kt │ │ │ │ ├── KotlinxJsonMapper.kt │ │ │ │ ├── RestApi.kt │ │ │ │ ├── handler │ │ │ │ ├── RestHandler.kt │ │ │ │ ├── collection │ │ │ │ │ ├── AbstractCollectionHandler.kt │ │ │ │ │ ├── AbstractExternalItemHandler.kt │ │ │ │ │ ├── AddCollectionHandler.kt │ │ │ │ │ ├── AddMediaItemHandler.kt │ │ │ │ │ ├── DeleteCollectionHandler.kt │ │ │ │ │ ├── DeleteExternalItemHandler.kt │ │ │ │ │ ├── DeleteMediaItemHandler.kt │ │ │ │ │ ├── FindExternalItemHandler.kt │ │ │ │ │ ├── ListCollectionHandler.kt │ │ │ │ │ ├── ListExternalItemHandler.kt │ │ │ │ │ ├── ListMediaItemHandler.kt │ │ │ │ │ ├── RandomMediaItemHandler.kt │ │ │ │ │ ├── ResolveMediaItemListByNameHandler.kt │ │ │ │ │ ├── ShowCollectionHandler.kt │ │ │ │ │ ├── ShowMediaItemHandler.kt │ │ │ │ │ ├── UpdateCollectionHandler.kt │ │ │ │ │ ├── UpdateMediaItemHandler.kt │ │ │ │ │ └── UploadExternalItemHandler.kt │ │ │ │ ├── download │ │ │ │ │ ├── AbstractDownloadHandler.kt │ │ │ │ │ ├── EvaluationDownloadHandler.kt │ │ │ │ │ ├── EvaluationTemplateDownloadHandler.kt │ │ │ │ │ └── ScoreDownloadHandler.kt │ │ │ │ ├── evaluation │ │ │ │ │ ├── admin │ │ │ │ │ │ ├── AbstractEvaluationAdminHandler.kt │ │ │ │ │ │ ├── AdjustDurationHandler.kt │ │ │ │ │ │ ├── AdjustPropertiesHandler.kt │ │ │ │ │ │ ├── CreateEvaluationHandler.kt │ │ │ │ │ │ ├── EvaluationOverviewHandler.kt │ │ │ │ │ │ ├── ForceViewerHandler.kt │ │ │ │ │ │ ├── GetEvaluationHandler.kt │ │ │ │ │ │ ├── ListPastTaskHandler.kt │ │ │ │ │ │ ├── ListSubmissionsHandler.kt │ │ │ │ │ │ ├── ListViewersHandler.kt │ │ │ │ │ │ ├── NextTaskHandler.kt │ │ │ │ │ │ ├── OverrideAnswerSetVerdictHandler.kt │ │ │ │ │ │ ├── PreviousTaskHandler.kt │ │ │ │ │ │ ├── StartEvaluationHandler.kt │ │ │ │ │ │ ├── StartTaskHandler.kt │ │ │ │ │ │ ├── StopEvaluationHandler.kt │ │ │ │ │ │ ├── StopTaskHandler.kt │ │ │ │ │ │ └── SwitchTaskHandler.kt │ │ │ │ │ ├── client │ │ │ │ │ │ ├── AbstractEvaluationClientHandler.kt │ │ │ │ │ │ ├── ClientListEvaluationsHandler.kt │ │ │ │ │ │ └── ClientTaskInfoHandler.kt │ │ │ │ │ ├── scores │ │ │ │ │ │ ├── AbstractScoreHandler.kt │ │ │ │ │ │ ├── CurrentTaskScoreHandler.kt │ │ │ │ │ │ ├── HistoryTaskScoreHandler.kt │ │ │ │ │ │ ├── ListEvaluationScoreHandler.kt │ │ │ │ │ │ ├── ListScoreSeriesHandler.kt │ │ │ │ │ │ ├── ListScoreboardsHandler.kt │ │ │ │ │ │ └── TeamGroupScoreHandler.kt │ │ │ │ │ ├── team │ │ │ │ │ │ ├── CreateTeamHandler.kt │ │ │ │ │ │ ├── ListAllTeamsHandler.kt │ │ │ │ │ │ └── UpdateTeamHandler.kt │ │ │ │ │ └── viewer │ │ │ │ │ │ ├── AbstractEvaluationViewerHandler.kt │ │ │ │ │ │ ├── GetCurrentTaskHandler.kt │ │ │ │ │ │ ├── GetEvaluationInfoHandler.kt │ │ │ │ │ │ ├── GetEvaluationStateHandler.kt │ │ │ │ │ │ ├── GetSubmissionAfterInfoHandler.kt │ │ │ │ │ │ ├── GetSubmissionHistoryInfoHandler.kt │ │ │ │ │ │ ├── GetSubmissionInfoHandler.kt │ │ │ │ │ │ ├── GetTaskHintHandler.kt │ │ │ │ │ │ ├── GetTaskTargetHandler.kt │ │ │ │ │ │ ├── ListEvaluationInfoHandler.kt │ │ │ │ │ │ ├── ListEvaluationStatesHandler.kt │ │ │ │ │ │ └── ViewerReadyHandler.kt │ │ │ │ ├── judgement │ │ │ │ │ ├── AbstractJudgementHandler.kt │ │ │ │ │ ├── DequeueJudgementHandler.kt │ │ │ │ │ ├── DequeueVoteHandler.kt │ │ │ │ │ ├── JudgementStatusHandler.kt │ │ │ │ │ ├── PostJudgementHandler.kt │ │ │ │ │ └── PostVoteHandler.kt │ │ │ │ ├── log │ │ │ │ │ ├── AbstractLogHandler.kt │ │ │ │ │ ├── QueryLogHandler.kt │ │ │ │ │ └── ResultLogHandler.kt │ │ │ │ ├── preview │ │ │ │ │ ├── AbstractPreviewHandler.kt │ │ │ │ │ ├── GetExternalMediaHandler.kt │ │ │ │ │ ├── GetMediaHandler.kt │ │ │ │ │ ├── PreviewImageHandler.kt │ │ │ │ │ ├── PreviewImageTimelessHandler.kt │ │ │ │ │ └── PreviewVideoHandler.kt │ │ │ │ ├── submission │ │ │ │ │ └── SubmissionHandler.kt │ │ │ │ ├── system │ │ │ │ │ ├── CurrentTimeHandler.kt │ │ │ │ │ ├── InfoHandler.kt │ │ │ │ │ ├── LoginHandler.kt │ │ │ │ │ └── LogoutHandler.kt │ │ │ │ ├── template │ │ │ │ │ ├── AbstractEvaluationTemplateHandler.kt │ │ │ │ │ ├── CloneEvaluationTemplateHandler.kt │ │ │ │ │ ├── CreateEvaluationTemplateHandler.kt │ │ │ │ │ ├── DeleteEvaluationTemplateHandler.kt │ │ │ │ │ ├── GetTeamLogoHandler.kt │ │ │ │ │ ├── ListEvaluationTemplatesHandler.kt │ │ │ │ │ ├── ListTaskTypePresetsHandler.kt │ │ │ │ │ ├── ListTasksHandler.kt │ │ │ │ │ ├── ListTeamHandler.kt │ │ │ │ │ ├── ShowEvaluationTemplateHandler.kt │ │ │ │ │ └── UpdateEvaluationTemplateHandler.kt │ │ │ │ └── users │ │ │ │ │ ├── AbstractUserHandler.kt │ │ │ │ │ ├── CreateUsersHandler.kt │ │ │ │ │ ├── DeleteUsersHandler.kt │ │ │ │ │ ├── ListActiveUsersHandler.kt │ │ │ │ │ ├── ListUsersHandler.kt │ │ │ │ │ ├── ShowCurrentSessionHandler.kt │ │ │ │ │ ├── ShowCurrentUserHandler.kt │ │ │ │ │ ├── UpdateUsersHandler.kt │ │ │ │ │ └── UserDetailsHandler.kt │ │ │ │ ├── types │ │ │ │ ├── AbstractRestEntity.kt │ │ │ │ ├── ViewerInfo.kt │ │ │ │ ├── collection │ │ │ │ │ ├── ApiMediaCollection.kt │ │ │ │ │ ├── ApiMediaItem.kt │ │ │ │ │ ├── ApiMediaItemMetaDataEntry.kt │ │ │ │ │ ├── ApiMediaSegment.kt │ │ │ │ │ ├── ApiMediaType.kt │ │ │ │ │ ├── ApiPopulatedMediaCollection.kt │ │ │ │ │ └── time │ │ │ │ │ │ ├── ApiTemporalPoint.kt │ │ │ │ │ │ ├── ApiTemporalRange.kt │ │ │ │ │ │ └── ApiTemporalUnit.kt │ │ │ │ ├── evaluation │ │ │ │ │ ├── ApiClientEvaluationInfo.kt │ │ │ │ │ ├── ApiClientTaskTemplateInfo.kt │ │ │ │ │ ├── ApiEvaluation.kt │ │ │ │ │ ├── ApiEvaluationInfo.kt │ │ │ │ │ ├── ApiEvaluationOverview.kt │ │ │ │ │ ├── ApiEvaluationState.kt │ │ │ │ │ ├── ApiEvaluationStatus.kt │ │ │ │ │ ├── ApiEvaluationType.kt │ │ │ │ │ ├── ApiOverrideAnswerSetVerdictDto.kt │ │ │ │ │ ├── ApiSubmissionInfo.kt │ │ │ │ │ ├── ApiTask.kt │ │ │ │ │ ├── ApiTaskOverview.kt │ │ │ │ │ ├── ApiTaskStatus.kt │ │ │ │ │ ├── ApiTaskTemplateInfo.kt │ │ │ │ │ ├── ApiTeamInfo.kt │ │ │ │ │ ├── ApiTeamTaskOverview.kt │ │ │ │ │ ├── ApiViewerInfo.kt │ │ │ │ │ ├── scores │ │ │ │ │ │ ├── ApiScore.kt │ │ │ │ │ │ ├── ApiScoreOverview.kt │ │ │ │ │ │ ├── ApiScoreSeries.kt │ │ │ │ │ │ ├── ApiScoreSeriesPoint.kt │ │ │ │ │ │ └── ApiTeamGroupValue.kt │ │ │ │ │ ├── submission │ │ │ │ │ │ ├── ApiAnswer.kt │ │ │ │ │ │ ├── ApiAnswerSet.kt │ │ │ │ │ │ ├── ApiAnswerType.kt │ │ │ │ │ │ ├── ApiClientAnswer.kt │ │ │ │ │ │ ├── ApiClientAnswerSet.kt │ │ │ │ │ │ ├── ApiClientSubmission.kt │ │ │ │ │ │ ├── ApiSubmission.kt │ │ │ │ │ │ └── ApiVerdictStatus.kt │ │ │ │ │ └── websocket │ │ │ │ │ │ ├── ClientMessage.kt │ │ │ │ │ │ ├── ClientMessageType.kt │ │ │ │ │ │ ├── ServerMessage.kt │ │ │ │ │ │ └── ServerMessageType.kt │ │ │ │ ├── judgement │ │ │ │ │ ├── ApiJudgement.kt │ │ │ │ │ ├── ApiJudgementRequest.kt │ │ │ │ │ ├── ApiJudgementValidatorStatus.kt │ │ │ │ │ └── ApiVote.kt │ │ │ │ ├── status │ │ │ │ │ ├── AbstractStatus.kt │ │ │ │ │ ├── ErrorStatus.kt │ │ │ │ │ ├── ErrorStatusException.kt │ │ │ │ │ ├── SuccessStatus.kt │ │ │ │ │ └── SuccessfulSubmissionsStatus.kt │ │ │ │ ├── submission │ │ │ │ │ ├── ResultElement.kt │ │ │ │ │ ├── RunResult.kt │ │ │ │ │ └── TaskResult.kt │ │ │ │ ├── system │ │ │ │ │ ├── CurrentTime.kt │ │ │ │ │ └── DresInfo.kt │ │ │ │ ├── task │ │ │ │ │ ├── ApiContentElement.kt │ │ │ │ │ └── ApiContentType.kt │ │ │ │ ├── template │ │ │ │ │ ├── ApiCreateEvaluation.kt │ │ │ │ │ ├── ApiEvaluationStartMessage.kt │ │ │ │ │ ├── ApiEvaluationTemplate.kt │ │ │ │ │ ├── ApiEvaluationTemplateOverview.kt │ │ │ │ │ ├── tasks │ │ │ │ │ │ ├── ApiHint.kt │ │ │ │ │ │ ├── ApiHintContent.kt │ │ │ │ │ │ ├── ApiHintType.kt │ │ │ │ │ │ ├── ApiTarget.kt │ │ │ │ │ │ ├── ApiTargetContent.kt │ │ │ │ │ │ ├── ApiTargetType.kt │ │ │ │ │ │ ├── ApiTaskGroup.kt │ │ │ │ │ │ ├── ApiTaskTemplate.kt │ │ │ │ │ │ ├── ApiTaskType.kt │ │ │ │ │ │ └── options │ │ │ │ │ │ │ ├── ApiHintOption.kt │ │ │ │ │ │ │ ├── ApiScoreOption.kt │ │ │ │ │ │ │ ├── ApiSubmissionOption.kt │ │ │ │ │ │ │ ├── ApiTargetOption.kt │ │ │ │ │ │ │ └── ApiTaskOption.kt │ │ │ │ │ └── team │ │ │ │ │ │ ├── ApiTeam.kt │ │ │ │ │ │ ├── ApiTeamAggregatorType.kt │ │ │ │ │ │ └── ApiTeamGroup.kt │ │ │ │ └── users │ │ │ │ │ ├── ApiRole.kt │ │ │ │ │ ├── ApiUser.kt │ │ │ │ │ └── ApiUserRequest.kt │ │ │ │ └── util │ │ │ │ └── MimeTypeHelper.kt │ │ │ ├── data │ │ │ ├── migration │ │ │ │ └── Migration.kt │ │ │ └── model │ │ │ │ ├── PersistentEntity.kt │ │ │ │ ├── admin │ │ │ │ ├── DbRole.kt │ │ │ │ ├── DbUser.kt │ │ │ │ ├── Password.kt │ │ │ │ └── User.kt │ │ │ │ ├── config │ │ │ │ ├── CacheConfig.kt │ │ │ │ └── Config.kt │ │ │ │ ├── log │ │ │ │ └── ParticipantLog.kt │ │ │ │ ├── media │ │ │ │ ├── DbMediaCollection.kt │ │ │ │ ├── DbMediaItem.kt │ │ │ │ ├── DbMediaItemMetaDataEntry.kt │ │ │ │ ├── DbMediaSegment.kt │ │ │ │ ├── DbMediaType.kt │ │ │ │ ├── MediaItem.kt │ │ │ │ ├── MediaItemCollection.kt │ │ │ │ ├── MediaItemType.kt │ │ │ │ └── time │ │ │ │ │ ├── TemporalPoint.kt │ │ │ │ │ └── TemporalRange.kt │ │ │ │ ├── run │ │ │ │ ├── AbstractEvaluation.kt │ │ │ │ ├── AbstractInteractiveTask.kt │ │ │ │ ├── AbstractNonInteractiveTask.kt │ │ │ │ ├── AbstractTask.kt │ │ │ │ ├── ApiRunProperties.kt │ │ │ │ ├── DbEvaluation.kt │ │ │ │ ├── DbEvaluationStatus.kt │ │ │ │ ├── DbEvaluationType.kt │ │ │ │ ├── DbTask.kt │ │ │ │ ├── DbTaskStatus.kt │ │ │ │ ├── InteractiveAsynchronousEvaluation.kt │ │ │ │ ├── InteractiveSynchronousEvaluation.kt │ │ │ │ ├── NonInteractiveEvaluation.kt │ │ │ │ ├── RunActionContext.kt │ │ │ │ ├── Task.kt │ │ │ │ └── interfaces │ │ │ │ │ ├── Evaluation.kt │ │ │ │ │ ├── EvaluationRun.kt │ │ │ │ │ ├── Run.kt │ │ │ │ │ └── TaskRun.kt │ │ │ │ ├── submissions │ │ │ │ ├── Answer.kt │ │ │ │ ├── AnswerSet.kt │ │ │ │ ├── AnswerType.kt │ │ │ │ ├── DbAnswer.kt │ │ │ │ ├── DbAnswerSet.kt │ │ │ │ ├── DbAnswerType.kt │ │ │ │ ├── DbSubmission.kt │ │ │ │ ├── DbVerdictStatus.kt │ │ │ │ ├── Submission.kt │ │ │ │ └── VerdictStatus.kt │ │ │ │ └── template │ │ │ │ ├── DbEvaluationTemplate.kt │ │ │ │ ├── interfaces │ │ │ │ └── EvaluationTemplate.kt │ │ │ │ ├── task │ │ │ │ ├── DbHint.kt │ │ │ │ ├── DbHintType.kt │ │ │ │ ├── DbTargetType.kt │ │ │ │ ├── DbTaskGroup.kt │ │ │ │ ├── DbTaskTemplate.kt │ │ │ │ ├── DbTaskTemplateTarget.kt │ │ │ │ ├── DbTaskType.kt │ │ │ │ ├── TaskTemplate.kt │ │ │ │ └── options │ │ │ │ │ ├── DbConfiguredOption.kt │ │ │ │ │ ├── DbHintOption.kt │ │ │ │ │ ├── DbScoreOption.kt │ │ │ │ │ ├── DbSubmissionOption.kt │ │ │ │ │ ├── DbTargetOption.kt │ │ │ │ │ ├── DbTaskOption.kt │ │ │ │ │ ├── Defaults.kt │ │ │ │ │ └── Parameters.kt │ │ │ │ └── team │ │ │ │ ├── DbTeam.kt │ │ │ │ ├── DbTeamAggregator.kt │ │ │ │ ├── DbTeamGroup.kt │ │ │ │ ├── Team.kt │ │ │ │ └── TeamAggregatorImpl.kt │ │ │ ├── mgmt │ │ │ ├── MediaCollectionManager.kt │ │ │ ├── TemplateManager.kt │ │ │ ├── admin │ │ │ │ └── UserManager.kt │ │ │ └── cache │ │ │ │ ├── AbstractPreviewRequest.kt │ │ │ │ └── CacheManager.kt │ │ │ ├── run │ │ │ ├── InteractiveAsynchronousRunManager.kt │ │ │ ├── InteractiveRunManager.kt │ │ │ ├── InteractiveSynchronousRunManager.kt │ │ │ ├── NonInteractiveRunManager.kt │ │ │ ├── RunExecutor.kt │ │ │ ├── RunManager.kt │ │ │ ├── RunManagerStatus.kt │ │ │ ├── audit │ │ │ │ ├── AuditLogEntry.kt │ │ │ │ ├── AuditLogSource.kt │ │ │ │ └── AuditLogger.kt │ │ │ ├── eventstream │ │ │ │ ├── EventStreamProcessor.kt │ │ │ │ ├── StreamEvent.kt │ │ │ │ └── StreamEventHandler.kt │ │ │ ├── exceptions │ │ │ │ ├── IllegalRunStateException.kt │ │ │ │ ├── IllegalTeamIdException.kt │ │ │ │ └── JudgementTimeoutException.kt │ │ │ ├── filter │ │ │ │ ├── DuplicateSubmissionFilter.kt │ │ │ │ ├── MaximumCorrectPerTeamFilter.kt │ │ │ │ ├── MaximumCorrectPerTeamMemberFilter.kt │ │ │ │ ├── MaximumTotalPerTeamFilter.kt │ │ │ │ ├── MaximumWrongPerTeamFilter.kt │ │ │ │ ├── SubmissionRateFilter.kt │ │ │ │ ├── SubmissionRejectedException.kt │ │ │ │ ├── ValidItemSubmissionFilter.kt │ │ │ │ ├── ValidTemporalSubmissionFilter.kt │ │ │ │ ├── ValidTextualSubmissionFilter.kt │ │ │ │ └── basics │ │ │ │ │ ├── AbstractSubmissionFilter.kt │ │ │ │ │ ├── AcceptAllSubmissionFilter.kt │ │ │ │ │ ├── AcceptNoneSubmissionFilter.kt │ │ │ │ │ ├── CombiningSubmissionFilter.kt │ │ │ │ │ └── SubmissionFilter.kt │ │ │ ├── score │ │ │ │ ├── ScoreTimePoint.kt │ │ │ │ ├── Scoreable.kt │ │ │ │ ├── scoreboard │ │ │ │ │ ├── MaxNormalizingScoreBoard.kt │ │ │ │ │ ├── Score.kt │ │ │ │ │ ├── ScoreOverview.kt │ │ │ │ │ └── Scoreboard.kt │ │ │ │ └── scorer │ │ │ │ │ ├── AbstractTaskScorer.kt │ │ │ │ │ ├── AvsTaskScorer.kt │ │ │ │ │ ├── CachingTaskScorer.kt │ │ │ │ │ ├── InferredAveragePrecisionScorer.kt │ │ │ │ │ ├── KisTaskScorer.kt │ │ │ │ │ ├── LegacyAvsTaskScorer.kt │ │ │ │ │ └── TaskScorer.kt │ │ │ ├── transformer │ │ │ │ ├── MapToSegmentTransformer.kt │ │ │ │ ├── SubmissionTaskMatchTransformer.kt │ │ │ │ └── basics │ │ │ │ │ ├── CombiningSubmissionTransformer.kt │ │ │ │ │ ├── IdentitySubmissionTransformer.kt │ │ │ │ │ └── SubmissionTransformer.kt │ │ │ ├── updatables │ │ │ │ ├── EndOnSubmitUpdatable.kt │ │ │ │ ├── Phase.kt │ │ │ │ ├── ProlongOnSubmitUpdatable.kt │ │ │ │ ├── StatefulUpdatable.kt │ │ │ │ └── Updatable.kt │ │ │ └── validation │ │ │ │ ├── ChainedAnswerSetValidator.kt │ │ │ │ ├── MediaItemsAnswerSetValidator.kt │ │ │ │ ├── TemporalContainmentAnswerSetValidator.kt │ │ │ │ ├── TextAnswerSetValidator.kt │ │ │ │ ├── interfaces │ │ │ │ ├── AnswerSetValidator.kt │ │ │ │ ├── JudgementValidator.kt │ │ │ │ └── VoteValidator.kt │ │ │ │ └── judged │ │ │ │ ├── BasicJudgementValidator.kt │ │ │ │ ├── BasicVoteValidator.kt │ │ │ │ └── ItemRange.kt │ │ │ └── utilities │ │ │ ├── CompletedFuture.kt │ │ │ ├── FailedFuture.kt │ │ │ ├── NamedThreadFactory.kt │ │ │ ├── ReadyLatch.kt │ │ │ └── extensions │ │ │ ├── ContextExtensions.kt │ │ │ ├── FileExtensions.kt │ │ │ ├── NumberExtensions.kt │ │ │ ├── StampedLockExtensions.kt │ │ │ └── StringExtensions.kt │ └── resources │ │ ├── dres-type-presets │ │ ├── 10_Textual-Known-Item-Search.json │ │ ├── 20_Visual-Known-Item-Search.json │ │ ├── 30_Ad-hoc-Video-Search.json │ │ ├── 40_LSC-Known-Item-Search.json │ │ ├── 50_LSC-Ad-hoc-Search.json │ │ └── 60_LSC-Q&A-Text.json │ │ ├── img │ │ ├── loading.png │ │ ├── missing.png │ │ └── text.png │ │ └── vote │ │ └── index.html │ └── test │ └── kotlin │ └── dres │ └── run │ └── score │ └── scorer │ ├── AvsTaskScorerTest.kt │ ├── KisTaskScorerTest.kt │ └── LegacyAvsTaskScorerTest.kt ├── build.gradle ├── config.json ├── doc ├── dres_mode_of_operation_dist_sync.png ├── dres_mode_of_operation_local_async.png ├── dres_mode_of_operation_local_sync.png ├── legacy.oas.json ├── oas-client.json ├── oas.json ├── quit_tmux_session.sh ├── start_or_get_tmux_session.sh └── start_tmux_session.sh ├── frontend ├── .browserslistrc ├── .eslintrc.json ├── .gitignore ├── .husky │ ├── common.sh │ ├── post-checkout │ ├── post-merge │ └── pre-commit ├── .prettierignore ├── .prettierrc.json ├── .stylelintrc.json ├── .yarn │ └── releases │ │ └── yarn-3.2.0.cjs ├── .yarnrc.yml ├── README.md ├── angular.json ├── build.gradle ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── karma.conf.js ├── openapitools.json ├── package.json ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.ts │ │ ├── app.config.ts │ │ ├── app.module.ts │ │ ├── collection │ │ │ ├── collection-builder │ │ │ │ ├── collection-builder-dialog │ │ │ │ │ ├── collection-builder-dialog.component.html │ │ │ │ │ ├── collection-builder-dialog.component.scss │ │ │ │ │ └── collection-builder-dialog.component.ts │ │ │ │ └── media-item-builder-dialog │ │ │ │ │ ├── media-item-builder-dialog.component.html │ │ │ │ │ ├── media-item-builder-dialog.component.scss │ │ │ │ │ └── media-item-builder-dialog.component.ts │ │ │ ├── collection-list │ │ │ │ ├── collection-list.component.html │ │ │ │ ├── collection-list.component.scss │ │ │ │ └── collection-list.component.ts │ │ │ ├── collection-viewer │ │ │ │ ├── collection-viewer.component.html │ │ │ │ ├── collection-viewer.component.scss │ │ │ │ └── collection-viewer.component.ts │ │ │ └── collection.module.ts │ │ ├── competition │ │ │ ├── competition-builder │ │ │ │ ├── competition-builder-task-dialog │ │ │ │ │ ├── advanced-builder-dialog │ │ │ │ │ │ ├── advanced-builder-dialog.component.html │ │ │ │ │ │ ├── advanced-builder-dialog.component.scss │ │ │ │ │ │ └── advanced-builder-dialog.component.ts │ │ │ │ │ ├── competition-builder-task-dialog.component.html │ │ │ │ │ ├── competition-builder-task-dialog.component.scss │ │ │ │ │ ├── competition-builder-task-dialog.component.ts │ │ │ │ │ ├── competition-form.builder.ts │ │ │ │ │ ├── require-match.ts │ │ │ │ │ ├── video-player-segment-builder-dialog │ │ │ │ │ │ ├── video-player-segment-builder-dialog.component.html │ │ │ │ │ │ ├── video-player-segment-builder-dialog.component.scss │ │ │ │ │ │ └── video-player-segment-builder-dialog.component.ts │ │ │ │ │ └── video-player-segment-builder │ │ │ │ │ │ ├── video-player-segment-builder.component.html │ │ │ │ │ │ ├── video-player-segment-builder.component.scss │ │ │ │ │ │ └── video-player-segment-builder.component.ts │ │ │ │ ├── competition-builder-task-group-dialog │ │ │ │ │ ├── competition-builder-task-group.component.html │ │ │ │ │ └── competition-builder-task-group.component.ts │ │ │ │ ├── competition-builder-task-type-dialog │ │ │ │ │ ├── competition-builder-task-type-dialog.component.html │ │ │ │ │ ├── competition-builder-task-type-dialog.component.scss │ │ │ │ │ └── competition-builder-task-type-dialog.component.ts │ │ │ │ ├── competition-builder.component.html │ │ │ │ ├── competition-builder.component.scss │ │ │ │ ├── competition-builder.component.ts │ │ │ │ └── competition-builder.module.ts │ │ │ └── competition.module.ts │ │ ├── error-handling │ │ │ ├── dres-backend-unauthorised-handler.service.ts │ │ │ ├── error-handling.module.ts │ │ │ └── global-error-handler.service.ts │ │ ├── error │ │ │ ├── error-dialog.service.ts │ │ │ ├── error-dialog │ │ │ │ ├── error-dialog.component.html │ │ │ │ ├── error-dialog.component.scss │ │ │ │ └── error-dialog.component.ts │ │ │ ├── error.module.ts │ │ │ ├── forbidden.component.ts │ │ │ └── not-found.component.ts │ │ ├── evaluation │ │ │ ├── admin │ │ │ │ └── submission │ │ │ │ │ ├── answer-set │ │ │ │ │ ├── answer-set.component.html │ │ │ │ │ ├── answer-set.component.scss │ │ │ │ │ └── answer-set.component.ts │ │ │ │ │ ├── answer │ │ │ │ │ ├── answer.component.html │ │ │ │ │ ├── answer.component.scss │ │ │ │ │ └── answer.component.ts │ │ │ │ │ ├── submissions-details │ │ │ │ │ ├── submissions-details.component.html │ │ │ │ │ ├── submissions-details.component.scss │ │ │ │ │ └── submissions-details.component.ts │ │ │ │ │ ├── submissions-list │ │ │ │ │ ├── submissions-list.component.html │ │ │ │ │ ├── submissions-list.component.scss │ │ │ │ │ └── submissions-list.component.ts │ │ │ │ │ └── template-info │ │ │ │ │ ├── template-info.component.html │ │ │ │ │ ├── template-info.component.scss │ │ │ │ │ └── template-info.component.ts │ │ │ ├── evaluation.module.ts │ │ │ └── task-controls │ │ │ │ ├── task-controls.component.html │ │ │ │ ├── task-controls.component.scss │ │ │ │ └── task-controls.component.ts │ │ ├── judgement │ │ │ ├── judgement-dialog │ │ │ │ ├── judgement-dialog-content.model.ts │ │ │ │ ├── judgement-dialog.component.html │ │ │ │ ├── judgement-dialog.component.scss │ │ │ │ └── judgement-dialog.component.ts │ │ │ ├── judgement-media-viewer.component.html │ │ │ ├── judgement-media-viewer.component.scss │ │ │ ├── judgement-media-viewer.component.ts │ │ │ ├── judgement-viewer.component.html │ │ │ ├── judgement-viewer.component.scss │ │ │ ├── judgement-viewer.component.ts │ │ │ ├── judgement-voting-viewer.component.html │ │ │ ├── judgement-voting-viewer.component.scss │ │ │ ├── judgement-voting-viewer.component.ts │ │ │ └── judgement.module.ts │ │ ├── model │ │ │ ├── access-checking.interface.ts │ │ │ ├── config.default.ts │ │ │ ├── config.interface.ts │ │ │ ├── user-group.model.ts │ │ │ └── ws │ │ │ │ ├── client-message-type.enum.ts │ │ │ │ ├── server-message-type.enum.ts │ │ │ │ ├── ws-client-message.interface.ts │ │ │ │ ├── ws-message.interface.ts │ │ │ │ └── ws-server-message.interface.ts │ │ ├── nonescaping-urlserializer.class.ts │ │ ├── run │ │ │ ├── abstract-run-list.component.ts │ │ │ ├── admin-run-list.component.html │ │ │ ├── admin-run-list.component.ts │ │ │ ├── run-admin-toolbar │ │ │ │ ├── run-admin-toolbar.component.html │ │ │ │ ├── run-admin-toolbar.component.scss │ │ │ │ └── run-admin-toolbar.component.ts │ │ │ ├── run-admin-view.component.html │ │ │ ├── run-admin-view.component.scss │ │ │ ├── run-admin-view.component.ts │ │ │ ├── run-async-admin-view │ │ │ │ ├── run-async-admin-view.component.html │ │ │ │ ├── run-async-admin-view.component.scss │ │ │ │ └── run-async-admin-view.component.ts │ │ │ ├── run-list.component.html │ │ │ ├── run-list.component.scss │ │ │ ├── run-list.component.ts │ │ │ ├── run.module.ts │ │ │ ├── score-history │ │ │ │ ├── run-score-history.component.html │ │ │ │ ├── run-score-history.component.scss │ │ │ │ └── run-score-history.component.ts │ │ │ ├── viewer-run-list.component.html │ │ │ └── viewer-run-list.component.ts │ │ ├── services │ │ │ ├── can-deactivate.guard.ts │ │ │ ├── logging │ │ │ │ ├── log-entry.model.ts │ │ │ │ ├── log-level.enum.ts │ │ │ │ ├── log-service.factory.ts │ │ │ │ ├── log-service.provider.ts │ │ │ │ ├── log.service.ts │ │ │ │ ├── logger-config.token.ts │ │ │ │ ├── logger.config.ts │ │ │ │ └── logging.module.ts │ │ │ ├── navigation │ │ │ │ ├── back-button.directive.ts │ │ │ │ ├── forward-button.directive.ts │ │ │ │ └── navigation.service.ts │ │ │ ├── pipes │ │ │ │ ├── enhance-task-past-info.pipe.ts │ │ │ │ ├── enhance-task-submission-info.pipe.ts │ │ │ │ ├── epoch2date.pipe.ts │ │ │ │ ├── filter-not-in.pipe.ts │ │ │ │ ├── format-media-item.pipe.ts │ │ │ │ ├── format-temporal-point.pipe.ts │ │ │ │ ├── format-temporal-unit.pipe.ts │ │ │ │ ├── format-time-pipe.pipe.ts │ │ │ │ ├── not-in-list-filter.pipe.ts │ │ │ │ ├── order-by.pipe.ts │ │ │ │ ├── resolve-media-item-preview.pipe.ts │ │ │ │ ├── resolve-media-item-url.pipe.ts │ │ │ │ ├── resolve-mediaitem.pipe.ts │ │ │ │ ├── resolve-team.pipe.ts │ │ │ │ ├── round-pipe.pipe.ts │ │ │ │ ├── space-to-newline.pipe.ts │ │ │ │ ├── submissions-of.pipe.ts │ │ │ │ └── underscore-wordbreak.pipe.ts │ │ │ ├── services.module.ts │ │ │ └── session │ │ │ │ ├── access-role.service.ts │ │ │ │ ├── authentication.sevice.ts │ │ │ │ └── guard.ts │ │ ├── shared │ │ │ ├── actionable-dynamic-table │ │ │ │ ├── actionable-dynamic-table.component.html │ │ │ │ ├── actionable-dynamic-table.component.scss │ │ │ │ └── actionable-dynamic-table.component.ts │ │ │ ├── api-status │ │ │ │ ├── api-status.component.html │ │ │ │ ├── api-status.component.scss │ │ │ │ └── api-status.component.ts │ │ │ ├── back-button │ │ │ │ ├── back-button.component.html │ │ │ │ ├── back-button.component.scss │ │ │ │ └── back-button.component.ts │ │ │ ├── confirmation-dialog │ │ │ │ ├── confirmation-dialog.component.html │ │ │ │ ├── confirmation-dialog.component.scss │ │ │ │ └── confirmation-dialog.component.ts │ │ │ ├── download-button │ │ │ │ ├── download-button.component.html │ │ │ │ ├── download-button.component.scss │ │ │ │ └── download-button.component.ts │ │ │ ├── dynamic-table │ │ │ │ ├── dynamic-table.component.html │ │ │ │ ├── dynamic-table.component.scss │ │ │ │ └── dynamic-table.component.ts │ │ │ ├── information-dialog │ │ │ │ ├── information-dialog.component.html │ │ │ │ ├── information-dialog.component.scss │ │ │ │ └── information-dialog.component.ts │ │ │ ├── media-item-viewer │ │ │ │ ├── media-item-viewer.component.html │ │ │ │ ├── media-item-viewer.component.scss │ │ │ │ └── media-item-viewer.component.ts │ │ │ ├── search-box │ │ │ │ ├── search-box.component.html │ │ │ │ ├── search-box.component.scss │ │ │ │ └── search-box.component.ts │ │ │ ├── server-info │ │ │ │ ├── server-info.component.html │ │ │ │ ├── server-info.component.scss │ │ │ │ └── server-info.component.ts │ │ │ ├── shared.module.ts │ │ │ ├── target-media-viewer │ │ │ │ ├── target-media-viewer.component.html │ │ │ │ ├── target-media-viewer.component.scss │ │ │ │ └── target-media-viewer.component.ts │ │ │ └── upload-json-button │ │ │ │ ├── upload-json-button.component.html │ │ │ │ ├── upload-json-button.component.scss │ │ │ │ └── upload-json-button.component.ts │ │ ├── template │ │ │ ├── evaluation-start-dialog │ │ │ │ ├── evaluation-start-dialog.component.html │ │ │ │ ├── evaluation-start-dialog.component.scss │ │ │ │ └── evaluation-start-dialog.component.ts │ │ │ ├── template-builder │ │ │ │ ├── components │ │ │ │ │ ├── abstract-template-builder.component.ts │ │ │ │ │ ├── batch-add-target-dialog │ │ │ │ │ │ ├── batch-add-target-dialog.component.html │ │ │ │ │ │ ├── batch-add-target-dialog.component.scss │ │ │ │ │ │ └── batch-add-target-dialog.component.ts │ │ │ │ │ ├── judges-list │ │ │ │ │ │ ├── judges-list.component.html │ │ │ │ │ │ ├── judges-list.component.scss │ │ │ │ │ │ └── judges-list.component.ts │ │ │ │ │ ├── query-description-external-form-field │ │ │ │ │ │ ├── query-description-external-form-field.component.html │ │ │ │ │ │ ├── query-description-external-form-field.component.scss │ │ │ │ │ │ └── query-description-external-form-field.component.ts │ │ │ │ │ ├── query-description-external-image-form-field │ │ │ │ │ │ ├── query-description-external-image-form-field.component.html │ │ │ │ │ │ ├── query-description-external-image-form-field.component.scss │ │ │ │ │ │ └── query-description-external-image-form-field.component.ts │ │ │ │ │ ├── query-description-external-video-form-field │ │ │ │ │ │ ├── query-description-external-video-form-field.component.html │ │ │ │ │ │ ├── query-description-external-video-form-field.component.scss │ │ │ │ │ │ └── query-description-external-video-form-field.component.ts │ │ │ │ │ ├── query-description-form-field │ │ │ │ │ │ ├── query-description-form-field.component.html │ │ │ │ │ │ ├── query-description-form-field.component.scss │ │ │ │ │ │ └── query-description-form-field.component.ts │ │ │ │ │ ├── query-description-media-item-form-field │ │ │ │ │ │ ├── query-description-media-item-form-field.component.html │ │ │ │ │ │ ├── query-description-media-item-form-field.component.scss │ │ │ │ │ │ └── query-description-media-item-form-field.component.ts │ │ │ │ │ ├── query-description-media-item-image-form-field │ │ │ │ │ │ ├── query-description-media-item-image-form-field.component.html │ │ │ │ │ │ ├── query-description-media-item-image-form-field.component.scss │ │ │ │ │ │ └── query-description-media-item-image-form-field.component.ts │ │ │ │ │ ├── query-description-media-item-video-form-field │ │ │ │ │ │ ├── query-description-media-item-video-form-field.component.html │ │ │ │ │ │ ├── query-description-media-item-video-form-field.component.scss │ │ │ │ │ │ └── query-description-media-item-video-form-field.component.ts │ │ │ │ │ ├── query-description-text-form-field │ │ │ │ │ │ ├── query-description-text-form-field.component.html │ │ │ │ │ │ ├── query-description-text-form-field.component.scss │ │ │ │ │ │ └── query-description-text-form-field.component.ts │ │ │ │ │ ├── task-groups-list │ │ │ │ │ │ ├── task-groups-list.component.html │ │ │ │ │ │ ├── task-groups-list.component.scss │ │ │ │ │ │ └── task-groups-list.component.ts │ │ │ │ │ ├── task-template-editor │ │ │ │ │ │ ├── task-template-editor.component.html │ │ │ │ │ │ ├── task-template-editor.component.scss │ │ │ │ │ │ └── task-template-editor.component.ts │ │ │ │ │ ├── task-types-list │ │ │ │ │ │ ├── task-types-list.component.html │ │ │ │ │ │ ├── task-types-list.component.scss │ │ │ │ │ │ └── task-types-list.component.ts │ │ │ │ │ ├── tasks-list │ │ │ │ │ │ ├── task-templates-list.component.html │ │ │ │ │ │ ├── task-templates-list.component.scss │ │ │ │ │ │ └── task-templates-list.component.ts │ │ │ │ │ ├── team-builder-dialog │ │ │ │ │ │ ├── team-builder-dialog.component.html │ │ │ │ │ │ ├── team-builder-dialog.component.scss │ │ │ │ │ │ ├── team-builder-dialog.component.ts │ │ │ │ │ │ ├── user-list-filter.pipe.ts │ │ │ │ │ │ └── user-list-in-other-filter.pipe.ts │ │ │ │ │ ├── teamgroups-dialog │ │ │ │ │ │ ├── teamgroups-dialog.component.html │ │ │ │ │ │ ├── teamgroups-dialog.component.scss │ │ │ │ │ │ └── teamgroups-dialog.component.ts │ │ │ │ │ ├── teamgroups-list │ │ │ │ │ │ ├── teamgroups-list.component.html │ │ │ │ │ │ ├── teamgroups-list.component.scss │ │ │ │ │ │ └── teamgroups-list.component.ts │ │ │ │ │ ├── teams-list │ │ │ │ │ │ ├── teams-list.component.html │ │ │ │ │ │ ├── teams-list.component.scss │ │ │ │ │ │ └── teams-list.component.ts │ │ │ │ │ ├── template-builder-components.module.ts │ │ │ │ │ ├── template-import-dialog │ │ │ │ │ │ ├── template-import-dialog.component.html │ │ │ │ │ │ ├── template-import-dialog.component.scss │ │ │ │ │ │ └── template-import-dialog.component.ts │ │ │ │ │ ├── template-import-tree │ │ │ │ │ │ ├── template-import-tree.component.html │ │ │ │ │ │ ├── template-import-tree.component.scss │ │ │ │ │ │ └── template-import-tree.component.ts │ │ │ │ │ ├── template-information │ │ │ │ │ │ ├── template-information.component.html │ │ │ │ │ │ ├── template-information.component.scss │ │ │ │ │ │ └── template-information.component.ts │ │ │ │ │ └── viewers-list │ │ │ │ │ │ ├── viewers-list.component.html │ │ │ │ │ │ ├── viewers-list.component.scss │ │ │ │ │ │ └── viewers-list.component.ts │ │ │ │ ├── template-builder.component.html │ │ │ │ ├── template-builder.component.scss │ │ │ │ ├── template-builder.component.ts │ │ │ │ ├── template-builder.module.ts │ │ │ │ └── template-builder.service.ts │ │ │ ├── template-create-dialog │ │ │ │ ├── template-create-dialog.component.html │ │ │ │ ├── template-create-dialog.component.scss │ │ │ │ └── template-create-dialog.component.ts │ │ │ ├── template-list │ │ │ │ ├── template-list.component.html │ │ │ │ ├── template-list.component.scss │ │ │ │ └── template-list.component.ts │ │ │ └── template.module.ts │ │ ├── user │ │ │ ├── admin-user-create-or-edit-dialog │ │ │ │ ├── admin-user-create-or-edit-dialog.component.html │ │ │ │ ├── admin-user-create-or-edit-dialog.component.scss │ │ │ │ └── admin-user-create-or-edit-dialog.component.ts │ │ │ ├── admin-user-list │ │ │ │ ├── admin-user-list.component.html │ │ │ │ ├── admin-user-list.component.scss │ │ │ │ └── admin-user-list.component.ts │ │ │ ├── login │ │ │ │ ├── login.component.html │ │ │ │ ├── login.component.scss │ │ │ │ └── login.component.ts │ │ │ ├── profile │ │ │ │ ├── profile.component.html │ │ │ │ ├── profile.component.scss │ │ │ │ └── profile.component.ts │ │ │ └── user.module.ts │ │ ├── utilities │ │ │ ├── api.utilities.ts │ │ │ ├── data.utilities.ts │ │ │ └── time.utilities.ts │ │ └── viewer │ │ │ ├── model │ │ │ ├── run-viewer-position.ts │ │ │ └── run-viewer-widgets.ts │ │ │ ├── query-object-preview │ │ │ ├── image-object-preview.component.ts │ │ │ ├── query-object-preview.module.ts │ │ │ ├── query-object.interface.ts │ │ │ ├── text-object-preview.component.ts │ │ │ └── video-object-preview.component.ts │ │ │ ├── run-viewer.component.html │ │ │ ├── run-viewer.component.scss │ │ │ ├── run-viewer.component.ts │ │ │ ├── scoreboard-viewer │ │ │ ├── scoreboard-viewer.component.html │ │ │ ├── scoreboard-viewer.component.scss │ │ │ └── scoreboard-viewer.component.ts │ │ │ ├── task-viewer.component.html │ │ │ ├── task-viewer.component.scss │ │ │ ├── task-viewer.component.ts │ │ │ ├── teams-viewer.component.html │ │ │ ├── teams-viewer.component.scss │ │ │ ├── teams-viewer.component.ts │ │ │ └── viewer.module.ts │ ├── config.json │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── flex.scss │ ├── immutable │ │ ├── android-icon-192x192.png │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── assets │ │ │ ├── audio │ │ │ │ ├── applause.ogg │ │ │ │ ├── beep_1.ogg │ │ │ │ ├── beep_2.ogg │ │ │ │ ├── correct.ogg │ │ │ │ ├── ding.ogg │ │ │ │ ├── glass.ogg │ │ │ │ ├── sad_trombone.ogg │ │ │ │ ├── whistle.ogg │ │ │ │ └── wrong.ogg │ │ │ └── images │ │ │ │ ├── load-icon.png │ │ │ │ ├── missing.png │ │ │ │ └── text.png │ │ ├── favicon-16x16.ico │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.ico │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.ico │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── mstile-150x150.png │ │ ├── mstile-310x310.png │ │ └── mstile-70x70.png │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── yarn.lock ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | *.exe filter=lfs diff=lfs merge=lfs -text 2 | *.dll filter=lfs diff=lfs merge=lfs -text 3 | *.so filter=lfs diff=lfs merge=lfs -text 4 | *.dylib filter=lfs diff=lfs merge=lfs -text 5 | ffmpeg filter=lfs diff=lfs merge=lfs -text 6 | ffplay filter=lfs diff=lfs merge=lfs -text 7 | ffprobe filter=lfs diff=lfs merge=lfs -text 8 | gradlew text eol=lf 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment** 27 | - OS: 28 | - Browser: 29 | - DRES Version: 30 | 31 | 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/issue-closed-labeler.yml: -------------------------------------------------------------------------------- 1 | # A workflow to use https://github.com/marketplace/actions/fixed-issues-labeler 2 | 3 | name: Label Closed Issues on Branches 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push on all but the master branch 8 | push: 9 | branches-ignore: 10 | - 'master' 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Label Fixed Issues 18 | uses: gh-bot/fix-labeler@master 19 | with: 20 | token: ${{ github.token }} 21 | label: 'dev' -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zenika/kotlin:1.4.20-jdk11 AS build 2 | 3 | COPY . /dres-src 4 | RUN cd /dres-src && \ 5 | ./gradlew distTar && \ 6 | mkdir dres-dist && \ 7 | cd dres-dist && \ 8 | tar xf ../backend/build/distributions/dres-dist.tar 9 | 10 | FROM zenika/kotlin:1.4.20-jdk11-slim 11 | 12 | RUN mkdir /dres-data 13 | COPY config.json /dres-data 14 | COPY --from=build /dres-src/dres-dist / 15 | 16 | EXPOSE 8080 17 | EXPOSE 8443 18 | ENTRYPOINT /dres-dist/bin/backend /dres-data/config.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Luca Rossetto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | Backend Component of DRES 2 | -------------------------------------------------------------------------------- /backend/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/backend/keystore.jks -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractExternalItemHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.handler.collection 2 | 3 | import dev.dres.api.rest.RestApi 4 | import dev.dres.api.rest.handler.AccessManagedRestHandler 5 | import dev.dres.api.rest.types.users.ApiRole 6 | import io.javalin.security.RouteRole 7 | 8 | abstract class AbstractExternalItemHandler : AccessManagedRestHandler { 9 | 10 | override val permittedRoles: Set = setOf(ApiRole.ADMIN) 11 | override val apiVersion: String = RestApi.LATEST_API_VERSION 12 | } 13 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.handler.download 2 | 3 | import dev.dres.api.rest.RestApi 4 | import dev.dres.api.rest.handler.AccessManagedRestHandler 5 | import dev.dres.api.rest.types.users.ApiRole 6 | import io.javalin.security.RouteRole 7 | import jetbrains.exodus.database.TransientEntityStore 8 | 9 | /** 10 | * A [AccessManagedRestHandler] implementation that provides certain data structures as downloadable files. 11 | * 12 | * @author Ralph Gasser 13 | * @version 1.0.0 14 | */ 15 | abstract class AbstractDownloadHandler : AccessManagedRestHandler { 16 | 17 | /** The version of the API this [AbstractDownloadHandler] belongs to. */ 18 | override val apiVersion = RestApi.LATEST_API_VERSION 19 | 20 | /** The roles permitted to access the [AbstractDownloadHandler]. */ 21 | override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) 22 | } 23 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.handler.evaluation.scores 2 | 3 | import dev.dres.api.rest.RestApi 4 | import dev.dres.api.rest.handler.AccessManagedRestHandler 5 | import dev.dres.api.rest.handler.RestHandler 6 | import dev.dres.api.rest.types.users.ApiRole 7 | import dev.dres.data.model.run.DbEvaluation 8 | import dev.dres.run.score.scoreboard.ScoreOverview 9 | import io.javalin.security.RouteRole 10 | import jetbrains.exodus.database.TransientEntityStore 11 | 12 | /** 13 | * A collection of [RestHandler]s that deal with [ScoreOverview]s for ongoing [DbEvaluation]s. 14 | * 15 | * @author Ralph Gasser 16 | * @version 2.0.0 17 | */ 18 | abstract class AbstractScoreHandler : RestHandler, AccessManagedRestHandler { 19 | /** By default [AbstractScoreHandler] can only be used by [ApiRole.VIEWER]. */ 20 | override val permittedRoles: Set = setOf(ApiRole.VIEWER) 21 | 22 | /** All [AbstractScoreHandler]s are part of the v1 API. */ 23 | override val apiVersion = RestApi.LATEST_API_VERSION 24 | } 25 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.handler.log 2 | 3 | import dev.dres.api.rest.RestApi 4 | import dev.dres.api.rest.handler.AccessManagedRestHandler 5 | import dev.dres.api.rest.handler.PostRestHandler 6 | import dev.dres.api.rest.types.status.SuccessStatus 7 | import dev.dres.api.rest.types.users.ApiRole 8 | import io.javalin.security.RouteRole 9 | 10 | /** 11 | * 12 | * @author Ralph Gasser 13 | * @version 1.0 14 | */ 15 | abstract class AbstractLogHandler: PostRestHandler, AccessManagedRestHandler { 16 | /** All [AbstractLogHandler]s are part of the v1 API. */ 17 | override val apiVersion = RestApi.LATEST_API_VERSION 18 | 19 | /** All [AbstractLogHandler]s require [ApiRole.ADMIN] or [ApiRole.PARTICIPANT]. */ 20 | override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) 21 | } 22 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.handler.system 2 | 3 | import dev.dres.api.rest.RestApi 4 | import dev.dres.api.rest.handler.GetRestHandler 5 | import dev.dres.api.rest.types.system.CurrentTime 6 | import io.javalin.http.Context 7 | import io.javalin.openapi.* 8 | 9 | /** 10 | * A [GetRestHandler] that returns the current server time. 11 | * 12 | * @author Luca Rossetto 13 | * @version 1.0.0 14 | */ 15 | class CurrentTimeHandler : GetRestHandler { 16 | 17 | override val route = "status/time" 18 | override val apiVersion = RestApi.LATEST_API_VERSION 19 | 20 | @OpenApi(summary = "Returns the current time on the server.", 21 | path = "/api/v2/status/time", 22 | operationId = OpenApiOperation.AUTO_GENERATE, 23 | methods = [HttpMethod.GET], 24 | tags = ["Status"], 25 | responses = [ 26 | OpenApiResponse("200", [OpenApiContent(CurrentTime::class)]) 27 | ]) 28 | override fun doGet(ctx: Context): CurrentTime = CurrentTime() 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/AbstractRestEntity.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types 2 | 3 | abstract class AbstractRestEntity(val id:String) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/ViewerInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types 2 | 3 | import dev.dres.api.rest.handler.users.SessionToken 4 | import dev.dres.utilities.extensions.realIP 5 | import dev.dres.utilities.extensions.sessionToken 6 | import io.javalin.http.Context 7 | 8 | data class ViewerInfo(val sessionToken: SessionToken, val host: String) { 9 | constructor(ctx: Context) : this(ctx.sessionToken()!!, ctx.realIP()) 10 | } 11 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.collection 2 | 3 | import dev.dres.data.model.media.CollectionId 4 | import dev.dres.data.model.media.DbMediaCollection 5 | import kotlinx.dnq.query.size 6 | 7 | /** 8 | * The RESTful API equivalent for [DbMediaCollection]. 9 | * 10 | * @see DbMediaCollection 11 | * @author Ralph Gasser 12 | * @version 1.0 13 | */ 14 | data class ApiMediaCollection( 15 | 16 | val id: CollectionId? = null, // or we create a ApiCreateMediaCollection dto ? 17 | val name: String, 18 | val description: String? = null, 19 | val basePath: String? = null, 20 | val itemCount: Int = 0 21 | 22 | ) { 23 | companion object { 24 | /** 25 | * Generates a [ApiMediaCollection] from a [DbMediaCollection] and returns it. 26 | * 27 | * @param collection The [DbMediaCollection] to convert. 28 | */ 29 | fun fromMediaCollection(collection: DbMediaCollection) = ApiMediaCollection(collection.id, collection.name, collection.description, collection.path, collection.items.size()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItemMetaDataEntry.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.collection 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class ApiMediaItemMetaDataEntry(val key: String, val value: String) 7 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaSegment.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.collection 2 | 3 | data class ApiMediaSegment(val mediaItemName: String, val segmentName: String, val start: Int, val end: Int) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.collection 2 | 3 | import dev.dres.data.model.media.DbMediaType 4 | import dev.dres.data.model.media.MediaItemType 5 | 6 | /** 7 | * The RESTful API equivalent for the type of a [ApiMediaItem] 8 | * 9 | * @see ApiMediaItem 10 | * @author Ralph Gasser 11 | * @version 1.0.0 12 | */ 13 | enum class ApiMediaType { 14 | IMAGE, VIDEO, TEXT; 15 | 16 | /** 17 | * Converts this [ApiMediaType] to a [DbMediaType] representation. Requires an ongoing transaction! 18 | * 19 | * @return [DbMediaType] 20 | */ 21 | fun toDb(): DbMediaType = when(this) { 22 | IMAGE -> DbMediaType.IMAGE 23 | VIDEO -> DbMediaType.VIDEO 24 | TEXT -> DbMediaType.TEXT 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiPopulatedMediaCollection.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.collection 2 | 3 | /** 4 | * A [ApiMediaCollection] populated with its items. No pagination, populated with all items. 5 | */ 6 | data class ApiPopulatedMediaCollection (val collection:ApiMediaCollection, val items: List) 7 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalRange.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.collection.time 2 | 3 | import dev.dres.data.model.media.time.TemporalRange 4 | import kotlinx.serialization.Serializable 5 | 6 | /** 7 | * RESTful API representation of a [TemporalRange]. 8 | * 9 | * @version 1.0.0 10 | * @author Ralph Gasser 11 | */ 12 | @Serializable 13 | data class ApiTemporalRange(val start: ApiTemporalPoint, val end: ApiTemporalPoint) { 14 | constructor(range: TemporalRange) : this(ApiTemporalPoint.fromTemporalPoint(range.start), ApiTemporalPoint.fromTemporalPoint(range.end)) 15 | fun toTemporalRange(fps: Float): TemporalRange = TemporalRange(start.toTemporalPoint(fps), end.toTemporalPoint(fps)) 16 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalUnit.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.collection.time 2 | 3 | /** 4 | * 5 | * @author Ralph Gasser 6 | * @version 1.0 7 | */ 8 | enum class ApiTemporalUnit { 9 | FRAME_NUMBER, 10 | SECONDS, 11 | MILLISECONDS, 12 | TIMECODE 13 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientTaskTemplateInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate 4 | import dev.dres.data.model.template.task.DbTaskTemplate 5 | import kotlinx.serialization.Serializable 6 | 7 | /** 8 | * Basic and most importantly static information about a [DbTaskTemplate] relevant to the participant. 9 | */ 10 | @Serializable 11 | data class ApiClientTaskTemplateInfo( 12 | val name: String, 13 | val taskGroup: String, 14 | val taskType: String, 15 | val duration: Long 16 | ) { 17 | constructor(task: ApiTaskTemplate) : this( 18 | task.name, 19 | task.taskGroup, 20 | task.taskType, 21 | task.duration 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.api.rest.types.template.ApiEvaluationTemplate 4 | import dev.dres.data.model.run.DbEvaluation 5 | import dev.dres.data.model.run.interfaces.Evaluation 6 | import dev.dres.data.model.run.interfaces.EvaluationId 7 | import kotlinx.serialization.Serializable 8 | 9 | /** 10 | * The RESTful API equivalent of a [DbEvaluation]. 11 | * 12 | * @author Luca Rossetto 13 | * @author Ralph Gasser 14 | * @version 2.0.0 15 | */ 16 | @Serializable 17 | data class ApiEvaluation( 18 | override val evaluationId: EvaluationId, 19 | val name: String, 20 | val type: ApiEvaluationType, 21 | val template: ApiEvaluationTemplate, 22 | val created: Long, 23 | val started: Long?, 24 | val ended: Long?, 25 | val tasks: List 26 | ) : Evaluation 27 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.data.model.run.DbEvaluationType 4 | /** 5 | * 6 | */ 7 | enum class ApiEvaluationType { 8 | SYNCHRONOUS, ASYNCHRONOUS, NON_INTERACTIVE; 9 | 10 | /** 11 | * Converts this [ApiEvaluationType] to a [DbEvaluationType] representation. Requires an ongoing transaction. 12 | * 13 | * @return [DbEvaluationType] 14 | */ 15 | fun toDb(): DbEvaluationType = when(this) { 16 | SYNCHRONOUS -> DbEvaluationType.INTERACTIVE_SYNCHRONOUS 17 | ASYNCHRONOUS -> DbEvaluationType.INTERACTIVE_ASYNCHRONOUS 18 | NON_INTERACTIVE -> DbEvaluationType.NON_INTERACTIVE 19 | } 20 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiOverrideAnswerSetVerdictDto.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus 4 | import kotlinx.serialization.Serializable 5 | 6 | /** 7 | * Data transfer object for overriding a [ApiAnswerSet]'s verdict. 8 | * 9 | * @see ApiAnswerSet 10 | * @author Loris Sauter 11 | * @version 1.0 12 | */ 13 | @Serializable 14 | data class ApiOverrideAnswerSetVerdictDto(val verdict: ApiVerdictStatus) 15 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiSubmission 4 | import dev.dres.data.model.run.interfaces.EvaluationId 5 | import dev.dres.data.model.template.TemplateId 6 | import kotlinx.serialization.Serializable 7 | 8 | /** 9 | * Encodes [ApiSubmission] data for a specific [EvaluationId] and (optionally) [EvaluationId]. 10 | * 11 | * @author Loris Sauter 12 | * @version 1.1.0 13 | */ 14 | @Serializable 15 | data class ApiSubmissionInfo(val evaluationId: EvaluationId, val taskId: TemplateId, val submissions: List) 16 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet 4 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 5 | import dev.dres.api.rest.types.evaluation.submission.ApiSubmission 6 | import dev.dres.data.model.run.DbTask 7 | import dev.dres.data.model.run.TaskId 8 | import dev.dres.data.model.template.TemplateId 9 | import kotlinx.serialization.Serializable 10 | 11 | /** 12 | * The RESTful API equivalent of a [DbTask]. 13 | * 14 | * @author Luca Rossetto 15 | * @author Ralph Gasser 16 | * @version 2.0.0 17 | */ 18 | @Serializable 19 | data class ApiTask( 20 | val taskId: TaskId, 21 | val templateId: TemplateId, 22 | val started: Long?, 23 | val ended: Long?, 24 | val submissions: List 25 | ) 26 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.data.model.run.interfaces.TaskRun 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class ApiTaskOverview( 8 | val id: String, 9 | val name: String, 10 | val type: String, 11 | val group: String, 12 | val duration: Long, 13 | val taskId: String, 14 | val status: ApiTaskStatus, 15 | val started: Long?, 16 | val ended: Long?) { 17 | constructor(task: TaskRun) : this( 18 | task.template.id!!, 19 | task.template.name, 20 | task.template.taskGroup, 21 | task.template.taskType, 22 | task.template.duration, 23 | task.taskId, 24 | task.status, 25 | task.started, 26 | task.ended) 27 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.data.model.run.DbTaskStatus 4 | 5 | 6 | /** The collection auf statuses an [ApiTask] can be in. 7 | * 8 | * API version of [DbTaskStatus] 9 | * 10 | * @author Ralph Gasser 11 | * @version 1.0.0 12 | */ 13 | enum class ApiTaskStatus { 14 | NO_TASK, CREATED, PREPARING, RUNNING, ENDED, IGNORED; 15 | 16 | /** 17 | * Converts this [ApiTaskStatus] to a [DbTaskStatus] representation. Requires an ongoing transaction. 18 | * 19 | * @return [Db] 20 | */ 21 | fun toDb(): DbTaskStatus = when(this) { 22 | CREATED -> DbTaskStatus.CREATED 23 | PREPARING -> DbTaskStatus.PREPARING 24 | RUNNING -> DbTaskStatus.RUNNING 25 | ENDED -> DbTaskStatus.ENDED 26 | IGNORED -> DbTaskStatus.IGNORED 27 | else -> throw IllegalArgumentException("The API task status $this cannot be converted to a persistent representation.") 28 | } 29 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate 4 | import dev.dres.data.model.template.task.DbTaskTemplate 5 | import kotlinx.serialization.Serializable 6 | 7 | /** 8 | * Basic and most importantly static information about a [DbTaskTemplate]. 9 | * 10 | * Since this information usually doesn't change in the course of a run, it allows for local caching and other optimizations. 11 | * 12 | * @author Ralph Gasser & Loris Sauter 13 | * @version 1.2.0 14 | */ 15 | @Serializable 16 | data class ApiTaskTemplateInfo( 17 | val templateId: String, 18 | val name: String, 19 | val comment: String?, 20 | val taskGroup: String, 21 | val taskType: String, 22 | val duration: Long 23 | ) { 24 | constructor(task: ApiTaskTemplate) : this( 25 | task.id!!, 26 | task.name, 27 | task.comment, 28 | task.taskGroup, 29 | task.taskType, 30 | task.duration 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import dev.dres.api.rest.types.template.team.ApiTeam 4 | import dev.dres.data.model.template.team.DbTeam 5 | import dev.dres.data.model.run.InteractiveSynchronousEvaluation 6 | import dev.dres.data.model.template.team.TeamId 7 | import kotlinx.serialization.Serializable 8 | 9 | /** 10 | * Basic and most importantly static information about the [DbTeam] partaking in a [InteractiveSynchronousEvaluation]. 11 | * Since this information usually doesn't change in the course of a run,t allows for local caching 12 | * and other optimizations. 13 | * 14 | * @author Ralph Gasser 15 | * @version 1.1.0 16 | */ 17 | 18 | @Serializable 19 | data class ApiTeamInfo(val id: TeamId, val name: String, val color: String) { 20 | constructor(team: ApiTeam) : this(team.id!!, team.name!!, team.color!!) 21 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamTaskOverview.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class ApiTeamTaskOverview(val teamId: String, val tasks: List) 7 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiViewerInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Basic information regarding a viewer instance 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.1.0 10 | */ 11 | @Serializable 12 | data class ApiViewerInfo(val viewersId: String, val username: String, val host: String, val ready: Boolean) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScore.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.scores 2 | 3 | import dev.dres.data.model.template.team.TeamId 4 | 5 | /** 6 | * A container class to track scores per team. 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | data class ApiScore(val teamId: TeamId, val score: Double) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreOverview.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.scores 2 | 3 | /** 4 | * A container class to scores for a specific score board. 5 | * 6 | * @author Ralph Gasser 7 | * @version 1.0.0 8 | */ 9 | data class ApiScoreOverview(val name: String, val taskGroup: String?, val scores: List) 10 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeries.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.scores 2 | 3 | /** 4 | * 5 | */ 6 | data class ApiScoreSeries(val team: String, val name: String, val points: List) 7 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeriesPoint.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.scores 2 | 3 | /** 4 | * 5 | */ 6 | class ApiScoreSeriesPoint(val score: Double, val timestamp: Long) 7 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiTeamGroupValue.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.scores 2 | 3 | /** 4 | * 5 | */ 6 | data class ApiTeamGroupValue(val name: String, val value: Double) 7 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.submission 2 | 3 | import dev.dres.data.model.submissions.DbAnswerType 4 | 5 | /** 6 | * The RESTful API equivalent for the type of a [DbAnswerType] 7 | * 8 | * @see ApiAnswerSet 9 | * @author Ralph Gasser 10 | * @version 1.0.0 11 | */ 12 | enum class ApiAnswerType { 13 | ITEM, TEMPORAL, TEXT; 14 | 15 | /** 16 | * Converts this [ApiAnswerType] to a [DbAnswerType] representation. Requires an ongoing transaction. 17 | * 18 | * @return [DbAnswerType] 19 | */ 20 | fun toDb(): DbAnswerType = when(this) { 21 | ITEM -> DbAnswerType.ITEM 22 | TEMPORAL -> DbAnswerType.TEMPORAL 23 | TEXT -> DbAnswerType.TEXT 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.submission 2 | 3 | 4 | import dev.dres.data.model.submissions.* 5 | import kotlinx.serialization.Serializable 6 | 7 | /** 8 | * The RESTful API equivalent of a submission as returned by the DRES endpoint. 9 | * 10 | * There is an inherent asymmetry between the submissions received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). 11 | * 12 | * @author Luca Rossetto 13 | * @author Ralph Gasser 14 | * @version 2.0.0 15 | */ 16 | @Serializable 17 | data class ApiSubmission( 18 | override val submissionId: SubmissionId, 19 | override val teamId: String, 20 | override val memberId: String, 21 | val teamName: String, 22 | val memberName: String, 23 | override val timestamp: Long, 24 | val answers: List = emptyList() 25 | ) : Submission { 26 | 27 | init { 28 | answers.forEach { it.submission = this } 29 | } 30 | override fun answerSets(): Sequence = answers.asSequence() 31 | 32 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiVerdictStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.submission 2 | 3 | import dev.dres.data.model.submissions.DbVerdictStatus 4 | 5 | /** 6 | * The RESTful API equivalent for the type of a [DbVerdictStatus] 7 | * 8 | * @see ApiAnswerSet 9 | * @author Ralph Gasser 10 | * @version 1.0.0 11 | */ 12 | enum class ApiVerdictStatus { 13 | CORRECT, WRONG, INDETERMINATE, UNDECIDABLE; 14 | 15 | /** 16 | * Converts this [ApiVerdictStatus] to a [DbVerdictStatus] representation. Requires an ongoing transaction. 17 | * 18 | * @return [DbVerdictStatus] 19 | */ 20 | fun toDb(): DbVerdictStatus = when(this) { 21 | CORRECT -> DbVerdictStatus.CORRECT 22 | WRONG -> DbVerdictStatus.WRONG 23 | INDETERMINATE -> DbVerdictStatus.INDETERMINATE 24 | UNDECIDABLE -> DbVerdictStatus.UNDECIDABLE 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.websocket 2 | 3 | /** 4 | * Message send by the DRES client via WebSocket to communicate with the DRES server. 5 | * 6 | * @author Ralph Gasser 7 | * @version 1.0 8 | */ 9 | data class ClientMessage(val evaluationId: String, val type: ClientMessageType) 10 | 11 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessageType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.websocket 2 | 3 | /** 4 | * Enumeration of the types of [ClientMessage]s. 5 | * 6 | * @version 1.0.0 7 | * @author Ralph Gasser 8 | */ 9 | enum class ClientMessageType { 10 | ACK, /** Acknowledgement of the last message received. */ 11 | REGISTER, 12 | UNREGISTER, 13 | PING 14 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.websocket 2 | 3 | import dev.dres.data.model.run.interfaces.EvaluationId 4 | import dev.dres.data.model.run.interfaces.TaskId 5 | 6 | /** 7 | * Message send by the DRES server via WebSocket to inform clients about the state of the run. 8 | * 9 | * @author Ralph Gasser 10 | * @version 1.2.0 11 | */ 12 | data class ServerMessage(val evaluationId: EvaluationId, val type: ServerMessageType, val taskId: TaskId? = null, val timestamp: Long = System.currentTimeMillis()) 13 | 14 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessageType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.evaluation.websocket 2 | 3 | /** 4 | * Enumeration of the types of [ServerMessage]s. 5 | * 6 | * @version 1.0.0 7 | * @author Ralph Gasser 8 | */ 9 | enum class ServerMessageType { 10 | COMPETITION_START, /** Competition run started. */ 11 | COMPETITION_UPDATE, /** State of competition run was updated. */ 12 | COMPETITION_END, /** Competition run ended. */ 13 | TASK_PREPARE, /** Prepare for a task run to start. */ 14 | TASK_START, /** Task run started. */ 15 | TASK_UPDATED, /** State of task run has changed; mostly handles arrival of or changes to submissions. */ 16 | TASK_END, /** Tasks run ended. */ 17 | PING /** Keep alive */ 18 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.judgement 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus 4 | 5 | /** 6 | * 7 | * @author Ralph Gasser 8 | * @version 1.0 9 | */ 10 | data class ApiJudgement(val token: String, val validator: String, val verdict: ApiVerdictStatus) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.judgement 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet 4 | 5 | /** 6 | * 7 | * DTO for judgement requests: 8 | * A submission that has to be judged by the client. 9 | * 10 | * @author Loris Sauter 11 | * @author Ralph Gasser 12 | * @version 2.0 13 | */ 14 | class ApiJudgementRequest( 15 | val token: String?, 16 | val validator: String, 17 | val taskDescription: String, 18 | val answerSet: ApiAnswerSet 19 | ) 20 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementValidatorStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.judgement 2 | 3 | /** 4 | * 5 | * @author Ralph Gasser 6 | * @version 1.0 7 | */ 8 | class ApiJudgementValidatorStatus(val validatorName: String, val pending: Int, val open: Int) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.judgement 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus 4 | 5 | /** 6 | * 7 | * @author Ralph Gasser 8 | * @version 1.0 9 | */ 10 | data class ApiVote(val verdict: ApiVerdictStatus) 11 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/status/AbstractStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.status 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | open class AbstractStatus(val status:Boolean){} -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.status 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class ErrorStatus(val description: String): AbstractStatus(status = false) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.status 2 | 3 | import io.javalin.http.Context 4 | import kotlinx.serialization.Serializable 5 | import org.slf4j.LoggerFactory 6 | 7 | @Serializable 8 | data class ErrorStatusException(val statusCode: Int, val status: String, private val ctx: Context, val doNotLog: Boolean = false) : Exception(status) { 9 | companion object { 10 | private val logger = LoggerFactory.getLogger(this::class.java) 11 | } 12 | init { 13 | if(!doNotLog){ 14 | logger.info("ErrorStatusException with code $statusCode and message '$status' thrown by ${stackTrace.first()} for request from ${ctx.req().remoteAddr}") 15 | } 16 | } 17 | val errorStatus: ErrorStatus 18 | get() = ErrorStatus(status) 19 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.status 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class SuccessStatus(val description: String): AbstractStatus(status = true) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.status 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class SuccessfulSubmissionsStatus(val submission: ApiVerdictStatus, val description: String) : AbstractStatus(status = true) 8 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/submission/ResultElement.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.submission 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties 4 | 5 | @JsonIgnoreProperties(ignoreUnknown = true) 6 | data class ResultElement( 7 | val item: String? = null, /** Name of the item */ 8 | val text: String? = null, /** Text submission */ 9 | val startTimeCode: String? = null, /** Starting time of a segment */ 10 | val endTimeCode: String? = null, /** End time of a segment */ 11 | val index: Int? = null, /** Index of a segment in case of predefined segmentation */ 12 | val rank: Int? = null, /** Rank of a result within a result list */ 13 | val weight: Float? = null /** Weight in case of weighted results */ 14 | ) { 15 | init { 16 | if (item.isNullOrBlank() && text.isNullOrBlank()) { 17 | throw IllegalArgumentException("ResultElement needs at least an item or text but neither was provided") 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/submission/RunResult.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.submission 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties 5 | import com.fasterxml.jackson.annotation.JsonSetter 6 | import com.fasterxml.jackson.annotation.Nulls 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | data class RunResult( 10 | @field:JsonSetter(contentNulls = Nulls.FAIL) 11 | val tasks: List, 12 | val timeStamp: Int = -1 13 | ) { 14 | @field:JsonIgnore 15 | @get:JsonIgnore 16 | internal val serverTimeStamp: Long = System.currentTimeMillis() 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/submission/TaskResult.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.submission 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties 4 | import com.fasterxml.jackson.annotation.JsonSetter 5 | import com.fasterxml.jackson.annotation.Nulls 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | data class TaskResult( 9 | @field:JsonSetter(contentNulls = Nulls.FAIL) 10 | val task: String, /** the name of the task */ 11 | val resultName: String = "default", /** optional name of a result set */ 12 | val resultType: String? = null, /** optional type information of a result set*/ 13 | @field:JsonSetter(contentNulls = Nulls.FAIL) 14 | val results: List /** list of actual results*/ 15 | ) 16 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/system/CurrentTime.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.system 2 | 3 | /** 4 | * The current timestamp. 5 | * 6 | * @author Luca Rossetto 7 | * @version 1.0.0 8 | */ 9 | data class CurrentTime(val timeStamp: Long = System.currentTimeMillis()) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/system/DresInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.system 2 | 3 | import dev.dres.DRES 4 | 5 | /** 6 | * Information about the DRES instance. 7 | * 8 | * @author Luca Rossetto 9 | * @version 1.0.0 10 | */ 11 | data class DresInfo( 12 | val version: String = DRES.VERSION, 13 | val startTime: Long, 14 | val uptime: Long, 15 | val os: String? = null, 16 | val jvm: String? = null, 17 | val args: String? = null, 18 | val cores: Int? = null, 19 | val freeMemory: Long? = null, 20 | val totalMemory: Long? = null, 21 | val load: Double? = null, 22 | val availableSeverThreads: Int? = null 23 | ) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentElement.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.task 2 | 3 | /** 4 | * Describes a [ApiContentElement], i.e., a piece of content that should be displayed to the user. 5 | * 6 | * @author Luca Rossetto & Ralph Gasser 7 | * @version 1.0.1 8 | * 9 | * @param content Encoded content; use [ApiContentType] to decode. 10 | * @param contentType [ApiContentType] of the content held by this [ApiContentElement] 11 | * @param offset Time in seconds after which this [ApiContentElement] should be displayed. 12 | */ 13 | data class ApiContentElement(val contentType: ApiContentType, val content: String? = null, val offset: Long) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.task 2 | 3 | /** 4 | * 5 | */ 6 | enum class ApiContentType(val mimeType: String, val base64: Boolean) { 7 | EMPTY("", false), 8 | TEXT("text/plain", false), 9 | VIDEO("video/mp4", true), 10 | IMAGE("image/jpg", true) 11 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiCreateEvaluation.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template 2 | 3 | /** 4 | * A data class that represents a RESTful request for creating a new [dres.data.model.competition.CompetitionDescription] 5 | * 6 | * @author Ralph Gasser 7 | * @version 1.0 8 | */ 9 | data class ApiCreateEvaluation(val name: String, val description: String) 10 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationStartMessage.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template 2 | 3 | import dev.dres.api.rest.types.evaluation.ApiEvaluationType 4 | import dev.dres.data.model.run.ApiRunProperties 5 | import dev.dres.data.model.template.TemplateId 6 | 7 | /** 8 | * A data class that represents a RESTful request for creating a new [dres.data.model.run.CompetitionRun] 9 | * 10 | * @author Ralph Gasser 11 | * @version 1.1.0 12 | */ 13 | data class ApiEvaluationStartMessage(val templateId: TemplateId, val name: String, val type: ApiEvaluationType, val properties: ApiRunProperties = ApiRunProperties()) 14 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationTemplateOverview.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template 2 | 3 | import dev.dres.data.model.template.DbEvaluationTemplate 4 | 5 | /** 6 | * An overview over a [DbEvaluationTemplate]. 7 | * 8 | * @author Ralph Gasser & Loris Sauter 9 | * @version 1.2.0 10 | */ 11 | data class ApiEvaluationTemplateOverview(val id: String, val name: String, val description: String?, val taskCount: Int, val teamCount: Int) 12 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHintContent.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks 2 | 3 | import dev.dres.api.rest.types.task.ApiContentElement 4 | 5 | /** 6 | * Describes a [ApiHintContent] that should be displayed the user as hint for a task run. This is a representation 7 | * used in the RESTful API to transfer information regarding the query. Always derived from a [TaskDescription]. 8 | * 9 | * @author Luca Rossetto & Ralph Gasser 10 | * @version 1.0 11 | * 12 | * @param taskId of the [TaskDescription] this [ApiHintContent] was derived from. 13 | * @param sequence Sequence of [ApiContentElement]s to display. 14 | * @param loop Specifies if last [ApiContentElement] should be displayed until the end or if the entire sequence should be looped. 15 | */ 16 | data class ApiHintContent(val taskId: String, val sequence: List = emptyList(), val loop: Boolean = false) 17 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHintType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks 2 | 3 | import dev.dres.data.model.template.task.DbHintType 4 | 5 | /** 6 | * The RESTful API equivalent for [DbHintType]. 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | enum class ApiHintType { 12 | EMPTY, TEXT, VIDEO, IMAGE; 13 | 14 | /** 15 | * Converts this [ApiHintType] to a [DbHintType] representation. Requires an ongoing transaction. 16 | * 17 | * @return [DbHintType] 18 | */ 19 | fun toDb(): DbHintType = when(this) { 20 | EMPTY -> DbHintType.EMPTY 21 | TEXT -> DbHintType.TEXT 22 | VIDEO -> DbHintType.VIDEO 23 | IMAGE -> DbHintType.IMAGE 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks 2 | 3 | import dev.dres.api.rest.types.collection.ApiMediaItem 4 | import dev.dres.api.rest.types.collection.time.ApiTemporalRange 5 | import dev.dres.data.model.template.task.DbTaskTemplateTarget 6 | import kotlinx.serialization.Serializable 7 | 8 | /** 9 | * The RESTful API equivalent for [DbTaskTemplateTarget]. 10 | * 11 | * @author Luca Rossetto & Ralph Gasser & Loris Sauter 12 | * @version 1.1.0 13 | */ 14 | @Serializable 15 | data class ApiTarget( 16 | val type: ApiTargetType, 17 | /** 18 | * The actual target, the semantic of this value depends on the type 19 | */ 20 | val target: String? = null, 21 | val range: ApiTemporalRange? = null, 22 | /** 23 | * The target as item, which is only defined for certain types (e.g. [ApiTargetType]-MEDIA_TIEM ) 24 | */ 25 | val item: ApiMediaItem? = null 26 | ) 27 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTargetContent.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks 2 | 3 | import dev.dres.api.rest.types.task.ApiContentElement 4 | 5 | /** 6 | * Describes a [ApiTargetContent] that should be displayed the user after the task run has finished. This is a representation 7 | * used in the RESTful API to transfer information regarding the query. Always derived from a [TaskDescription]. 8 | * 9 | * @author Luca Rossetto & Ralph Gasser 10 | * @version 1.0 11 | * 12 | * @param taskId of the [TaskDescription] this [ApiTargetContent] was derived from. 13 | * @param sequence Sequence of [ApiContentElement]s to display. 14 | */ 15 | data class ApiTargetContent(val taskId: String, val sequence: List = emptyList()) 16 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTargetType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks 2 | 3 | import dev.dres.data.model.template.task.DbTargetType 4 | 5 | /** 6 | * The RESTful API equivalent for [DbTargetType]. 7 | * 8 | * @author Luca Rossetto & Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | enum class ApiTargetType { 12 | JUDGEMENT, JUDGEMENT_WITH_VOTE, MEDIA_ITEM, MEDIA_ITEM_TEMPORAL_RANGE, TEXT; 13 | 14 | /** 15 | * Converts this [ApiTargetType] to a [DbTargetType] representation. Requires an ongoing transaction. 16 | * 17 | * @return [DbTargetType] 18 | */ 19 | fun toDb(): DbTargetType = when(this) { 20 | JUDGEMENT -> DbTargetType.JUDGEMENT 21 | JUDGEMENT_WITH_VOTE -> DbTargetType.JUDGEMENT_WITH_VOTE 22 | MEDIA_ITEM -> DbTargetType.MEDIA_ITEM 23 | MEDIA_ITEM_TEMPORAL_RANGE -> DbTargetType.MEDIA_ITEM_TEMPORAL_RANGE 24 | TEXT -> DbTargetType.TEXT 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskGroup.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks 2 | 3 | import dev.dres.data.model.template.task.DbTaskGroup 4 | import kotlinx.serialization.Serializable 5 | 6 | /** 7 | * The RESTful API equivalent of a [DbTaskGroup]. 8 | * 9 | * @author Ralph Gasser 10 | * @version 1.0.0 11 | */ 12 | @Serializable 13 | data class ApiTaskGroup(val id: String?,val name: String, val type: String) 14 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/options/ApiHintOption.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks.options 2 | 3 | import dev.dres.data.model.template.task.options.DbHintOption 4 | 5 | /** 6 | * A RESTful API representation of [DbHintOption]. 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | enum class ApiHintOption { 12 | IMAGE_ITEM, VIDEO_ITEM_SEGMENT, TEXT, EXTERNAL_IMAGE, EXTERNAL_VIDEO; 13 | 14 | /** 15 | * Converts this [ApiHintOption] to a RESTful API representation [DbHintOption]. 16 | * 17 | * @return [DbHintOption] 18 | */ 19 | fun toDb() = when(this) { 20 | IMAGE_ITEM -> DbHintOption.IMAGE_ITEM 21 | VIDEO_ITEM_SEGMENT -> DbHintOption.VIDEO_ITEM_SEGMENT 22 | TEXT -> DbHintOption.TEXT 23 | EXTERNAL_IMAGE -> DbHintOption.EXTERNAL_IMAGE 24 | EXTERNAL_VIDEO -> DbHintOption.EXTERNAL_VIDEO 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/options/ApiScoreOption.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks.options 2 | 3 | import dev.dres.data.model.template.task.options.DbScoreOption 4 | 5 | /** 6 | * A RESTful API representation of [DbScoreOption]. 7 | * 8 | * @author Ralph Gasser 9 | * @author Loris Sauter 10 | * @version 1.1.0 11 | */ 12 | enum class ApiScoreOption { 13 | KIS, AVS, LEGACY_AVS; 14 | 15 | /** 16 | * Converts this [ApiScoreOption] to a [DbScoreOption] representation. Requires an ongoing transaction. 17 | * 18 | * @return [DbScoreOption] 19 | */ 20 | fun toDb(): DbScoreOption = when(this) { 21 | KIS -> DbScoreOption.KIS 22 | AVS -> DbScoreOption.AVS 23 | LEGACY_AVS -> DbScoreOption.LEGACY_AVS 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/options/ApiTargetOption.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks.options 2 | 3 | import dev.dres.data.model.template.task.options.DbTargetOption 4 | 5 | /** 6 | * A RESTful API representation of [DbTargetOption]. 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | enum class ApiTargetOption{ 12 | SINGLE_MEDIA_ITEM, SINGLE_MEDIA_SEGMENT, JUDGEMENT, VOTE, TEXT; 13 | 14 | /** 15 | * Converts this [ApiTargetOption] to a [DbTargetOption] representation. Requires an ongoing transaction. 16 | * 17 | * @return [DbTargetOption] 18 | */ 19 | fun toDb(): DbTargetOption = when(this) { 20 | SINGLE_MEDIA_ITEM -> DbTargetOption.MEDIA_ITEM 21 | SINGLE_MEDIA_SEGMENT -> DbTargetOption.MEDIA_SEGMENT 22 | JUDGEMENT -> DbTargetOption.JUDGEMENT 23 | VOTE -> DbTargetOption.VOTE 24 | TEXT -> DbTargetOption.TEXT 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/options/ApiTaskOption.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.tasks.options 2 | 3 | import dev.dres.data.model.template.task.options.DbTaskOption 4 | 5 | /** 6 | * A RESTful API representation of [DbTaskOption]. 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | enum class ApiTaskOption { 12 | HIDDEN_RESULTS, MAP_TO_SEGMENT, PROLONG_ON_SUBMISSION; 13 | 14 | /** 15 | * Converts this [ApiTaskOption] to a [DbTaskOption] representation. Requires an ongoing transaction. 16 | * 17 | * @return [DbTaskOption] 18 | */ 19 | fun toDb(): DbTaskOption = when(this) { 20 | HIDDEN_RESULTS -> DbTaskOption.HIDDEN_RESULTS 21 | MAP_TO_SEGMENT -> DbTaskOption.MAP_TO_SEGMENT 22 | PROLONG_ON_SUBMISSION -> DbTaskOption.PROLONG_ON_SUBMISSION 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamGroup.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.template.team 2 | 3 | import dev.dres.data.model.template.team.DbTeamGroup 4 | import dev.dres.data.model.template.team.TeamAggregatorImpl 5 | import dev.dres.data.model.template.team.TeamGroupId 6 | import kotlinx.serialization.Serializable 7 | 8 | /** 9 | * A RESTful API representation of a [DbTeamGroup] 10 | * 11 | * @author Loris Sauter 12 | * @version 1.0.0 13 | */ 14 | @Serializable 15 | data class ApiTeamGroup( 16 | val id: TeamGroupId? = null, 17 | val name: String? = null, 18 | val teams: List = emptyList(), 19 | val aggregation: ApiTeamAggregatorType 20 | ) { 21 | /** 22 | * Returns a new [TeamAggregatorImpl] for this [DbTeamGroup]. 23 | * 24 | * This is a convenience method and requires an active transaction context. 25 | * 26 | * @return [TeamAggregatorImpl] 27 | */ 28 | fun newAggregator() : TeamAggregatorImpl = this.aggregation.newInstance(this.teams.map { it.teamId }.toSet()) 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.users 2 | 3 | import dev.dres.data.model.admin.DbUser 4 | import dev.dres.data.model.admin.UserId 5 | import kotlinx.serialization.Serializable 6 | 7 | /** 8 | * A RESTful API representation of a [DbUser] 9 | * 10 | * @author Loris Sauter 11 | * @version 1.0.0 12 | */ 13 | @Serializable 14 | data class ApiUser(val id: UserId? = null, val username: String? = null, val role: ApiRole? = null, var sessionId: String? = null) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUserRequest.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.api.rest.types.users 2 | 3 | /** 4 | * A request surrounding manipulation of users. 5 | * 6 | * @author Loris Sauter 7 | * @version 1.0.0 8 | */ 9 | data class ApiUserRequest(val username: String, val password: String?, val role: ApiRole?) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/migration/Migration.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.migration 2 | 3 | import dev.dres.data.model.config.Config 4 | import jetbrains.exodus.database.TransientEntityStore 5 | 6 | /** 7 | * General interface implemented by data migration scripts. 8 | * 9 | * TODO: Extend 10 | * 11 | * @author Ralph Gasser 12 | * @version 1.1.0 13 | */ 14 | interface Migration { 15 | /** 16 | * Performs data migration. 17 | * 18 | * @param config The global [Config] used for the data migration. 19 | * @param store The [TransientEntityStore] used for data access. 20 | */ 21 | fun migrate(config: Config, store: TransientEntityStore) 22 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model 2 | 3 | import jetbrains.exodus.entitystore.Entity 4 | import kotlinx.dnq.XdEntity 5 | import kotlinx.dnq.XdNaturalEntityType 6 | import kotlinx.dnq.xdRequiredStringProp 7 | import java.util.* 8 | 9 | /** 10 | * The root class for all DRES entities that are persisted via Xodus DNQ. 11 | * 12 | * @author Ralph Gasser 13 | * @version 1.0.0 14 | */ 15 | abstract class PersistentEntity(entity: Entity): XdEntity(entity) { 16 | companion object: XdNaturalEntityType() 17 | var id: String by xdRequiredStringProp(unique = true, trimmed = false) 18 | 19 | override fun constructor() { 20 | this.id = UUID.randomUUID().toString() 21 | } 22 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.admin 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator 4 | import com.fasterxml.jackson.annotation.JsonProperty 5 | import org.mindrot.jbcrypt.BCrypt 6 | 7 | /** 8 | * A [Password]. Exists in two variations [Plain] and [Hashed]. 9 | * 10 | * 11 | * @author Ralph Gasser 12 | * @version 1.0.0 13 | */ 14 | sealed class Password(val password: String) { 15 | class Plain(password: String): Password(password) { 16 | init { 17 | require (password.length >= DbUser.MIN_LENGTH_PASSWORD) { "Password is too short. Must be at least ${DbUser.MIN_LENGTH_PASSWORD} characters long" } 18 | } 19 | fun hash(): Hashed = Hashed(BCrypt.hashpw(this.password, BCrypt.gensalt())) 20 | val length: Int 21 | get() = this.password.length 22 | } 23 | 24 | class Hashed @JsonCreator constructor (@JsonProperty("hash") hash: String) : Password(hash) { 25 | fun check(plain: Plain): Boolean = BCrypt.checkpw(plain.password, this.password) 26 | } 27 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/admin/User.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.admin 2 | 3 | typealias UserId = String 4 | 5 | interface User { //TODO 6 | 7 | val userId: UserId 8 | val username: String 9 | 10 | 11 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItemMetaDataEntry.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.media 2 | 3 | import dev.dres.api.rest.types.collection.ApiMediaItemMetaDataEntry 4 | import jetbrains.exodus.entitystore.Entity 5 | import kotlinx.dnq.XdEntity 6 | import kotlinx.dnq.XdNaturalEntityType 7 | import kotlinx.dnq.xdParent 8 | import kotlinx.dnq.xdRequiredStringProp 9 | 10 | internal class DbMediaItemMetaDataEntry(entity: Entity) : XdEntity(entity) { 11 | 12 | companion object: XdNaturalEntityType() 13 | 14 | /** The key for this [DbMediaItemMetaDataEntry]. */ 15 | var key by xdRequiredStringProp() 16 | 17 | /** The value for this [DbMediaItemMetaDataEntry]. */ 18 | var value by xdRequiredStringProp() 19 | 20 | /** The [DbMediaItem] this [DbMediaItemMetaDataEntry] belongs to. */ 21 | val item: DbMediaItem by xdParent(DbMediaItem::metadata) 22 | 23 | fun asPair() = key to value 24 | 25 | fun toApi() = ApiMediaItemMetaDataEntry(key, value) 26 | 27 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.media 2 | 3 | typealias MediaItemId = String 4 | 5 | interface MediaItem { 6 | 7 | /** */ 8 | val name: String 9 | 10 | /** */ 11 | val mediaItemId: MediaItemId 12 | 13 | /** */ 14 | fun dbCollection(): MediaItemCollection 15 | 16 | /** */ 17 | fun type(): MediaItemType 18 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.media 2 | 3 | typealias CollectionId = String 4 | 5 | interface MediaItemCollection { 6 | 7 | val id: CollectionId 8 | 9 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.media 2 | 3 | import dev.dres.api.rest.types.collection.ApiMediaType 4 | 5 | enum class MediaItemType { 6 | IMAGE, VIDEO, TEXT; 7 | 8 | fun toApi() = when(this) { 9 | IMAGE -> ApiMediaType.IMAGE 10 | VIDEO -> ApiMediaType.VIDEO 11 | TEXT -> ApiMediaType.TEXT 12 | } 13 | 14 | fun toDb() = when(this) { 15 | IMAGE -> DbMediaType.IMAGE 16 | VIDEO -> DbMediaType.VIDEO 17 | TEXT -> DbMediaType.TEXT 18 | } 19 | 20 | companion object { 21 | 22 | fun fromApi(type: ApiMediaType) = when(type) { 23 | ApiMediaType.IMAGE -> IMAGE 24 | ApiMediaType.VIDEO -> VIDEO 25 | ApiMediaType.TEXT -> TEXT 26 | } 27 | 28 | fun fromDb(type: DbMediaType) = when(type) { 29 | DbMediaType.IMAGE -> IMAGE 30 | DbMediaType.VIDEO -> VIDEO 31 | DbMediaType.TEXT -> TEXT 32 | else -> throw IllegalStateException("Unknown DbMediaType $type") 33 | } 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/run/ApiRunProperties.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.run 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * 7 | */ 8 | @Serializable 9 | data class ApiRunProperties ( 10 | val participantCanView: Boolean = true, 11 | val shuffleTasks: Boolean = false, //is only used for asynchronous runs 12 | val allowRepeatedTasks: Boolean = false, 13 | val limitSubmissionPreviews: Int = -1 14 | ) 15 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/run/Task.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.run 2 | 3 | import dev.dres.data.model.run.interfaces.Evaluation 4 | import dev.dres.data.model.submissions.AnswerSet 5 | import dev.dres.data.model.template.task.TaskTemplate 6 | 7 | typealias TaskId = String 8 | 9 | interface Task { //TODO 10 | val taskId: TaskId 11 | val started: Long? 12 | val ended: Long? 13 | val evaluation: Evaluation 14 | val template: TaskTemplate 15 | 16 | fun answerSets(): Sequence 17 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.run.interfaces 2 | 3 | typealias EvaluationId = String 4 | 5 | interface Evaluation { 6 | val evaluationId: EvaluationId 7 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.submissions 2 | 3 | import dev.dres.data.model.media.MediaItem 4 | import dev.dres.data.model.media.time.TemporalPoint 5 | import dev.dres.data.model.media.time.TemporalRange 6 | /** 7 | * A [Answer] as issued by a DRES user as part of a [AnswerSet]. 8 | * 9 | * This abstraction is mainly required to enable testability of implementations. 10 | * 11 | * @author Luca Rossetto 12 | * @author Ralph Gasser 13 | * @version 2.0.0 14 | */ 15 | interface Answer { 16 | 17 | val item: MediaItem? 18 | val start: Long? 19 | val end: Long? 20 | val text: String? 21 | 22 | /** Returns the [TemporalRange] for this [DbAnswerSet]. */ 23 | val temporalRange: TemporalRange? 24 | get() { 25 | val start = this.start ?: return null 26 | val end = this.end ?: return null 27 | return TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) 28 | } 29 | 30 | /** 31 | * 32 | */ 33 | fun type(): AnswerType 34 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.submissions 2 | 3 | import dev.dres.data.model.run.TaskId 4 | 5 | typealias AnswerSetId = String 6 | 7 | /** 8 | * An [AnswerSet] as issued by a DRES user as part of a [Submission]. 9 | * 10 | * This abstraction is mainly required to enable testability of implementations. 11 | * 12 | * @author Luca Rossetto 13 | * @author Ralph Gasser 14 | * @version 2.0.0 15 | */ 16 | interface AnswerSet { 17 | /** The ID of this [AnswerSet]. */ 18 | val id : AnswerSetId 19 | 20 | /** The ID of the task this [AnswerSet] belongs to. */ 21 | val taskId: TaskId 22 | 23 | /** The [Submission] this [AnswerSet] belongs to. */ 24 | val submission: Submission 25 | 26 | /** 27 | * The [VerdictStatus] of this [AnswerSet]. 28 | * 29 | * @return The [VerdictStatus] of this [AnswerSet]. 30 | */ 31 | fun status(): VerdictStatus 32 | 33 | /** 34 | * Returns a [Sequence] of [Answer]s for this [AnswerSet]. 35 | */ 36 | fun answers() : Sequence 37 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.submissions 2 | 3 | import dev.dres.data.model.admin.UserId 4 | import dev.dres.data.model.template.team.TeamId 5 | 6 | typealias SubmissionId = String 7 | 8 | /** 9 | * A [Submission] as issued by a DRES user. 10 | * Essentially a proposed solution to a certain task. The task's reference is linked via the answer sets. 11 | * 12 | * This abstraction is mainly required to enable testability of implementations. 13 | * 14 | * @author Luca Rossetto 15 | * @author Ralph Gasser 16 | * @author Loris Sauter 17 | * @version 2.0.0 18 | */ 19 | interface Submission { 20 | /** The ID of this [Submission]. */ 21 | val submissionId: SubmissionId 22 | 23 | /** The ID of the user who issued this [Submission]. */ 24 | val memberId: UserId 25 | 26 | /** The ID of the team, the user belongs to. */ 27 | val teamId: TeamId 28 | 29 | /** The timestamp of this [Submission]. */ 30 | val timestamp: Long 31 | 32 | /** 33 | * Returns a [Sequence] of [AnswerSet]s. 34 | */ 35 | fun answerSets(): Sequence 36 | } 37 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/template/interfaces/EvaluationTemplate.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.template.interfaces 2 | 3 | import dev.dres.data.model.template.TemplateId 4 | 5 | interface EvaluationTemplate { 6 | 7 | val templateId: TemplateId 8 | 9 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.template.task 2 | 3 | import dev.dres.data.model.template.TemplateId 4 | 5 | interface TaskTemplate { 6 | fun textualDescription(): String 7 | 8 | val templateId: TaskTemplateId 9 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.template.task.options 2 | 3 | object Defaults { 4 | /** Default value for [Parameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM]. Defaults to the rule being applied during the final 5 seconds. */ 5 | const val PROLONG_ON_SUBMISSION_LIMIT_DEFAULT = 5 6 | 7 | /** Default value for [Parameters.PROLONG_ON_SUBMISSION_BY_PARAM]. Defaults to a prolongation of 5 seconds. */ 8 | const val PROLONG_ON_SUBMISSION_BY_DEFAULT = 5 9 | 10 | const val PROLONG_ON_SUBMISSION_CORRECT_DEFAULT = false 11 | 12 | /** Default interval for scoreboard updates in ms. TODO: Make configurable. */ 13 | const val SCOREBOARD_UPDATE_INTERVAL_DEFAULT = 5000L 14 | 15 | /** Default timeout for viewer in ms. TODO: make configurable */ 16 | const val VIEWER_TIMEOUT_DEFAULT = 30_000L 17 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/template/task/options/Parameters.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.template.task.options 2 | 3 | object Parameters { 4 | /** 5 | * Key for [DbTaskOption.PROLONG_ON_SUBMISSION]; [Int] value expected. 6 | * 7 | * The parameter determines how many milliseconds before the end of a task the option should be applied. 8 | * If a task has a total duration of 500s, then a value of 5s means that submissions arriving between 495s and 500s 9 | * may trigger prolongation of the task. 10 | */ 11 | const val PROLONG_ON_SUBMISSION_LIMIT_PARAM = "PROLONG_ON_SUBMISSION_LIMIT" 12 | 13 | /** Key for [DbTaskOption.PROLONG_ON_SUBMISSION]; [Int] value expected. Determines by how many seconds a task should be prolonged. */ 14 | const val PROLONG_ON_SUBMISSION_BY_PARAM = "PROLONG_ON_SUBMISSION_BY" 15 | 16 | /** Key for [DbTaskOption.PROLONG_ON_SUBMISSION]; [Boolean] value expected. If true, task will only be prolonged for correct submissions. */ 17 | const val PROLONG_ON_SUBMISSION_CORRECT_PARAM = "PROLONG_ON_SUBMISSION_CORRECT" 18 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.data.model.template.team 2 | 3 | /** The ID of a [DbTeam]. */ 4 | typealias TeamId = String 5 | 6 | interface Team { //TODO 7 | 8 | val teamId: TeamId? 9 | 10 | } 11 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/mgmt/cache/AbstractPreviewRequest.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.mgmt.cache 2 | 3 | import java.nio.file.Files 4 | import java.nio.file.Path 5 | import java.util.concurrent.Callable 6 | 7 | /** 8 | * 9 | * @author Ralph Gasser 10 | * @version 1.0 11 | */ 12 | abstract class AbstractPreviewRequest(protected val input: Path, protected val output: Path): Callable { 13 | 14 | init { 15 | require(Files.exists(this.input)) { "Could not generate preview because file $input does not exist." } 16 | if (!Files.exists(output.parent)) { 17 | Files.createDirectories(output.parent) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/RunManagerStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run 2 | 3 | import dev.dres.api.rest.types.evaluation.ApiEvaluationStatus 4 | 5 | /** 6 | * The collection auf statuses a [RunManager] can be in. 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | enum class RunManagerStatus { 12 | /** [RunManager] was created and has not started yet. It can now be setup. */ 13 | CREATED, 14 | 15 | /** [RunManager] was started and setup; it is ready to run [Task]s. */ 16 | ACTIVE, 17 | 18 | /** [RunManager] was terminated and cannot run anymore [Task]s. */ 19 | TERMINATED; 20 | 21 | /** 22 | * Converts this [RunManagerStatus] to the corresponding [ApiEvaluationStatus]. 23 | * 24 | * @return [ApiEvaluationStatus] 25 | */ 26 | fun toApi(): ApiEvaluationStatus = when (this) { 27 | CREATED -> ApiEvaluationStatus.CREATED 28 | ACTIVE -> ApiEvaluationStatus.ACTIVE 29 | TERMINATED -> ApiEvaluationStatus.TERMINATED 30 | } 31 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/audit/AuditLogSource.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.audit 2 | 3 | enum class AuditLogSource { 4 | REST, 5 | CLI, 6 | INTERNAL 7 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/eventstream/StreamEventHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.eventstream 2 | 3 | interface StreamEventHandler { 4 | 5 | fun handleStreamEvent(event: StreamEvent) 6 | 7 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/exceptions/IllegalRunStateException.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.exceptions 2 | 3 | import dev.dres.run.RunManager 4 | import dev.dres.run.RunManagerStatus 5 | 6 | /** 7 | * An [IllegalStateException] that gets thrown whenever a [RunManager] is not in the right [RunManagerStatus] to execute a command. 8 | * Errors like this are usually linked to bad user input. 9 | * 10 | * @author Ralph Gasser 11 | * @version 1.0.0 12 | */ 13 | class IllegalRunStateException(status: RunManagerStatus) : IllegalStateException("Could not execute request because run manager is in wrong state (s = $status).") 14 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.exceptions 2 | 3 | import dev.dres.data.model.template.team.TeamId 4 | import dev.dres.run.RunManager 5 | 6 | /** 7 | * An [IllegalStateException] that gets thrown whenever a [RunManager] or a dependent class does not know a [TeamId] it is supposed to process. 8 | * 9 | * Errors like this are usually linked to bad user input. 10 | * 11 | * @author Ralph Gasser 12 | * @version 1.0.0 13 | */ 14 | class IllegalTeamIdException(val teamId: TeamId) : IllegalStateException("Could not execute request because run manager does not know the given team ID $teamId.") -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/exceptions/JudgementTimeoutException.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.exceptions 2 | 3 | /** 4 | * A [IllegalArgumentException] to throw whenever a [JudgementValidator] cannot judge due to a timeout. 5 | * In particular, this exception should be thrown to indicate the judgement 6 | * verdict received will not be applied, since it took too long. 7 | * 8 | * @author Loris Sauter 9 | * @version 1.0.0 10 | */ 11 | class JudgementTimeoutException(msg: String) : IllegalArgumentException(msg) 12 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.filter 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 4 | import dev.dres.data.model.submissions.Submission 5 | 6 | /** 7 | * An exception that is thrown, when a [Submission] is rejected by a [SubmissionFilter]. 8 | * 9 | * @author Ralph Gasser 10 | * @version 1.0.0 11 | */ 12 | class SubmissionRejectedException(s: ApiClientSubmission, reason: String) : Throwable("Submission ${s.submissionId} was rejected by filter: $reason") -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptAllSubmissionFilter.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.filter.basics 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 4 | 5 | 6 | /** 7 | * A [SubmissionFilter] that lets all [ApiClientSubmission] pass. 8 | * 9 | * @author Ralph Gasser 10 | * @version 1.0.0 11 | */ 12 | object AcceptAllSubmissionFilter : SubmissionFilter { 13 | /** 14 | * Tests the given [ApiClientSubmission] with this [AcceptAllSubmissionFilter]. 15 | * 16 | * @param submission The [ApiClientSubmission] to check. 17 | */ 18 | override fun acceptOrThrow(submission: ApiClientSubmission) { 19 | /* No op. */ 20 | } 21 | 22 | /** 23 | * Evaluates this [SubmissionFilter] on the given argument. Always returns true. 24 | * 25 | * @param submission The input argument. 26 | * @return True 27 | */ 28 | override fun test(submission: ApiClientSubmission): Boolean = true 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptNoneSubmissionFilter.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.filter.basics 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 4 | import dev.dres.run.filter.SubmissionRejectedException 5 | 6 | /** 7 | * A [SubmissionFilter] that lets no [ApiClientSubmission] pass. 8 | * 9 | * @author Ralph Gasser 10 | * @version 1.0.0 11 | */ 12 | object AcceptNoneSubmissionFilter: SubmissionFilter { 13 | /** 14 | * Tests the given [ApiClientSubmission] with this [AcceptAllSubmissionFilter]. 15 | * 16 | * @param submission The [ApiClientSubmission] to check. 17 | */ 18 | override fun acceptOrThrow(submission: ApiClientSubmission) { 19 | throw SubmissionRejectedException(submission, "No submission are accepted!") 20 | } 21 | 22 | /** 23 | * Evaluates this [SubmissionFilter] on the given argument. Always returns true. 24 | * 25 | * @param submission The input argument. 26 | * @return True 27 | */ 28 | override fun test(submission: ApiClientSubmission): Boolean = false 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/filter/basics/SubmissionFilter.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.filter.basics 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 4 | import dev.dres.data.model.run.AbstractTask 5 | import dev.dres.data.model.submissions.Submission 6 | import dev.dres.run.filter.SubmissionRejectedException 7 | import java.util.function.Predicate 8 | 9 | /** 10 | * A [Predicate] that can be used to filter [Submission]'s prior to them being processed 11 | * by the [Submission] evaluation pipeline. 12 | * 13 | * @author Ralph Gasser 14 | * @version 2.0.0 15 | */ 16 | interface SubmissionFilter : Predicate { 17 | /** 18 | * Tests the given [ApiClientSubmission] for the provided [AbstractTask] with this [SubmissionFilter] and throws a [SubmissionRejectedException], if the test fails. 19 | * 20 | * @param submission The [ApiClientSubmission] to check. 21 | * @throws SubmissionRejectedException on failure 22 | */ 23 | fun acceptOrThrow(submission: ApiClientSubmission) 24 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/score/ScoreTimePoint.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.score 2 | 3 | import dev.dres.run.score.scoreboard.Score 4 | import java.rmi.server.UID 5 | 6 | /** 7 | * Container class to track Scores over time 8 | */ 9 | data class ScoreTimePoint(val name: String, val team: String, val score: Double, val timestamp: Long = System.currentTimeMillis()) { 10 | constructor(name: String, score: Score, timestamp: Long = System.currentTimeMillis()) : this(name, score.teamId, score.score, timestamp) 11 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/score/Scoreable.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.score 2 | 3 | import dev.dres.data.model.run.Task 4 | import dev.dres.data.model.run.interfaces.TaskId 5 | import dev.dres.data.model.template.team.TeamId 6 | 7 | /** 8 | * A [Scoreable] is subject of a [TaskScorer]. 9 | * 10 | * @author Ralph Gasser 11 | * @version 1.0.0 12 | */ 13 | interface Scoreable { 14 | /** The [TaskId] of the [Task] masked by this [Scoreable]. */ 15 | val taskId: TaskId 16 | 17 | /** The [TeamId]s of teams that work on the task identified by this [Scoreable]. */ 18 | val teams: List 19 | 20 | /** Duration of when the [Task] in seconds. */ 21 | val duration: Long 22 | 23 | /** Timestamp of when the [Task] identified by this [Scoreable] was started. */ 24 | val started: Long? 25 | 26 | /** Timestamp of when the [Task] identified by this [Scoreable] ended. */ 27 | val ended: Long? 28 | } 29 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.score.scoreboard 2 | 3 | import dev.dres.api.rest.types.evaluation.scores.ApiScore import dev.dres.data.model.template.team.TeamId 4 | 5 | /** 6 | * A container class to track scores per team. 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | data class Score(val teamId: TeamId, val score: Double) { 12 | 13 | /** 14 | * Converts this [Score] to a RESTful API representation [ApiScore]. 15 | * 16 | * @return [ApiScore] 17 | */ 18 | fun toApi(): ApiScore = ApiScore(this.teamId, this.score) 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.score.scoreboard 2 | 3 | import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview 4 | 5 | /** 6 | * A container class to scores for a specific score board. 7 | * 8 | * @author Ralph Gasser 9 | * @version 1.0.0 10 | */ 11 | data class ScoreOverview(val name: String, val taskGroup: String?, val scores: List) { 12 | 13 | /** 14 | * Converts this [ScoreOverview] to a RESTful API representation [ApiScoreOverview]. 15 | * 16 | * @return [ApiScoreOverview] 17 | */ 18 | fun toApi() = ApiScoreOverview(this.name, this.taskGroup, this.scores.map { it.toApi() }) 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.score.scorer 2 | 3 | import dev.dres.data.model.submissions.Submission 4 | import dev.dres.data.model.template.team.TeamId 5 | import dev.dres.run.score.Scoreable 6 | 7 | /** Type alias for a */ 8 | typealias ScoreEntry = Triple 9 | 10 | /** 11 | * A [TaskScorer] that re-computes the current scores of all teams for a given task based on the entire submission history. 12 | * 13 | * @author Luca Rossetto 14 | * @author Ralph Gasser 15 | * @version 2.0.0 16 | */ 17 | interface TaskScorer { 18 | 19 | /** The [Scoreable] this [TaskScorer] is scoring. */ 20 | val scoreable: Scoreable 21 | 22 | /** 23 | * Returns List of [ScoreEntry]s as generated by this [TaskScorer] 24 | * 25 | * @return A [List] of [ScoreEntry] 26 | */ 27 | fun scores(): List = this.scoreMap().map { ScoreEntry(it.key, null, it.value) } 28 | 29 | /** 30 | * Returns a map of [TeamId] to score as generated by this [TaskScorer] 31 | * 32 | * @return A [Map] of [TeamId] to score value 33 | */ 34 | fun scoreMap(): Map 35 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchTransformer.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.transformer 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 4 | import dev.dres.data.model.run.TaskId 5 | import dev.dres.data.model.submissions.DbSubmission 6 | import dev.dres.run.transformer.basics.SubmissionTransformer 7 | import kotlinx.dnq.query.iterator 8 | 9 | /** 10 | * A [SubmissionTransformer] that removes all [AnswerSet]s from a [Submission] that do not match a specified [TaskId] 11 | * 12 | * @author Luca Rossetto 13 | */ 14 | class SubmissionTaskMatchTransformer(private val taskId: TaskId) : SubmissionTransformer { 15 | /** 16 | * Apply this [SubmissionTaskMatchTransformer] to the provided [ApiClientSubmission]. 17 | * 18 | * Requires an ongoing transaction. 19 | * 20 | * @param submission [ApiClientSubmission] to transform. 21 | */ 22 | override fun transform(submission: ApiClientSubmission): ApiClientSubmission { 23 | 24 | return submission.copy( 25 | answerSets = submission.answerSets.filter { it.taskId == this.taskId } 26 | ) 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/transformer/basics/CombiningSubmissionTransformer.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.transformer.basics 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 4 | import dev.dres.data.model.submissions.DbSubmission 5 | 6 | /** 7 | * A [SubmissionTransformer] for [DbSubmission]s that combines multiple [SubmissionTransformer]. 8 | * 9 | * @author Luca Rossetto 10 | */ 11 | class CombiningSubmissionTransformer(private val transformers: List) : SubmissionTransformer { 12 | 13 | /** 14 | * Apply this [CombiningSubmissionTransformer] to the provided [DbSubmission]. Transformation happens in place. 15 | * 16 | * Requires an ongoing transaction if nested [SubmissionTransformer]s require an ongoing transaction. 17 | * 18 | * @param submission [DbSubmission] to transform. 19 | */ 20 | override fun transform(submission: ApiClientSubmission) : ApiClientSubmission { 21 | var sub = submission 22 | for (transformer in transformers) { 23 | sub = transformer.transform(sub) 24 | } 25 | return sub 26 | } 27 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/transformer/basics/IdentitySubmissionTransformer.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.transformer.basics 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 4 | import dev.dres.data.model.submissions.DbSubmission 5 | 6 | /** 7 | * A [SubmissionTransformer] that does not make any changes. 8 | * 9 | * @author Luca Rossetto 10 | */ 11 | object IdentitySubmissionTransformer : SubmissionTransformer { 12 | /** 13 | * Apply this [IdentitySubmissionTransformer] to the provided [DbSubmission]. 14 | * 15 | * @param submission [DbSubmission] to transform. 16 | */ 17 | override fun transform(submission: ApiClientSubmission) : ApiClientSubmission = submission 18 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/transformer/basics/SubmissionTransformer.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.transformer.basics 2 | 3 | import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission 4 | import dev.dres.data.model.submissions.DbSubmission 5 | 6 | 7 | /** 8 | * A transformer for [DbSubmission]s. 9 | * 10 | * @author Luca Rossetto 11 | */ 12 | interface SubmissionTransformer { 13 | 14 | /** 15 | * Apply this [SubmissionTransformer] to the provided [ApiClientSubmission]. 16 | * 17 | * Usually requires an ongoing transaction. 18 | * 19 | * @param submission [ApiClientSubmission] to transform. 20 | */ 21 | fun transform(submission: ApiClientSubmission) : ApiClientSubmission 22 | 23 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/updatables/Phase.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.updatables 2 | 3 | /** 4 | * Abstraction of a [Phase] within a [RunManager]'s run loop. Each iteration undergoes the [Phase]s 5 | * in the order [PREPARE], [MAIN] and [FINALIZE]. 6 | * 7 | * @author Ralph Gasser 8 | * @version 1.0.0 9 | */ 10 | enum class Phase { 11 | PREPARE, /** Preparation [Phase] in the run loop. Used to build data structures or prepare data. */ 12 | MAIN, /** Main [Phase] in the run loop. Used to update internal state e.g. recalculate scoreboards. */ 13 | FINALIZE /** Finalization [Phase] in the run loop. Used to send out messages or persist information. */ 14 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/updatables/StatefulUpdatable.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.updatables 2 | 3 | /** 4 | * An [Updatable] that has an internal state regarding its dirtyness. 5 | * 6 | * @author Ralph Gasser 7 | * @version 1.0 8 | */ 9 | interface StatefulUpdatable : Updatable { 10 | /** Flag indicating whether this [StatefulUpdatable] requires an update or not */ 11 | var dirty: Boolean 12 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.validation.interfaces 2 | 3 | import dev.dres.data.model.submissions.DbAnswerSet 4 | import dev.dres.data.model.submissions.DbSubmission 5 | 6 | /** 7 | * A validator class that checks, if a [DbSubmission] is correct. 8 | * 9 | * @author Luca Rossetto 10 | * @author Ralph Gasser 11 | * @version 2.0.0 12 | */ 13 | interface AnswerSetValidator { 14 | /** 15 | * Indicates whether this [AnswerSetValidator] needs to defer the validation to some later point in time or changes the status of a submission immediately. 16 | */ 17 | val deferring: Boolean 18 | 19 | /** 20 | * Validates the [DbAnswerSet] and updates its [DBVerdictStatus]. 21 | * 22 | * Usually requires an ongoing transaction. 23 | * 24 | * @param answerSet The [DbAnswerSet] to validate. 25 | */ 26 | fun validate(answerSet: DbAnswerSet) 27 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.run.validation.judged 2 | 3 | import dev.dres.data.model.media.DbMediaItem 4 | import dev.dres.data.model.submissions.Answer 5 | import dev.dres.data.model.submissions.AnswerType 6 | import dev.dres.data.model.submissions.DbAnswer 7 | import dev.dres.data.model.submissions.DbAnswerType 8 | 9 | /** 10 | * Helper class to store submission information independent of source. 11 | * 12 | * @author Luca Rossetto 13 | * @version 2.0.0 14 | */ 15 | data class ItemRange(val element: String, val start: Long, val end: Long) { 16 | constructor(item: DbMediaItem): this(item.id, 0, 0) 17 | constructor(item: DbMediaItem, start: Long, end: Long): this(item.id, start, end) 18 | constructor(answer: Answer): this(when (answer.type()){ 19 | AnswerType.ITEM, 20 | AnswerType.TEMPORAL -> answer.item!!.mediaItemId 21 | AnswerType.TEXT -> answer.text!! 22 | }, answer.start ?: 0, answer.end ?: 0) 23 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/utilities/CompletedFuture.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.utilities 2 | 3 | import java.util.concurrent.Future 4 | import java.util.concurrent.TimeUnit 5 | 6 | /** 7 | * A [Future] implementation that encapsulates an completed value. 8 | * 9 | * @author Ralph Gasser 10 | * @version 1.0.0 11 | */ 12 | data class CompletedFuture(private val value: T): Future { 13 | override fun cancel(mayInterruptIfRunning: Boolean): Boolean = false 14 | override fun isCancelled(): Boolean = false 15 | override fun isDone(): Boolean =true 16 | override fun get(): T = this.value 17 | override fun get(timeout: Long, unit: TimeUnit): T = this.get() 18 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/utilities/FailedFuture.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.utilities 2 | 3 | import java.util.concurrent.Future 4 | import java.util.concurrent.TimeUnit 5 | 6 | /** 7 | * A [Future] implementation that encapsulates a failed request. 8 | * 9 | * @author Ralph Gasser 10 | * @version 1.0.0 11 | */ 12 | data class FailedFuture(private val message: String): Future { 13 | override fun cancel(mayInterruptIfRunning: Boolean): Boolean = false 14 | override fun isCancelled(): Boolean = false 15 | override fun isDone(): Boolean = true 16 | override fun get(): T = throw IllegalStateException(message) 17 | override fun get(timeout: Long, unit: TimeUnit): T = this.get() 18 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/utilities/NamedThreadFactory.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.utilities 2 | 3 | import java.util.concurrent.ThreadFactory 4 | import java.util.concurrent.atomic.AtomicInteger 5 | 6 | class NamedThreadFactory(private val poolName: String) : ThreadFactory { 7 | private val group: ThreadGroup 8 | private val threadNumber = AtomicInteger(1) 9 | override fun newThread(r: Runnable): Thread { 10 | val t = Thread( 11 | group, r, 12 | "$poolName-thread-${threadNumber.getAndIncrement()}", 13 | 0 14 | ) 15 | if (t.isDaemon) t.isDaemon = false 16 | if (t.priority != Thread.NORM_PRIORITY) t.priority = Thread.NORM_PRIORITY 17 | return t 18 | } 19 | 20 | init { 21 | val s = System.getSecurityManager() 22 | group = if (s != null) s.threadGroup else Thread.currentThread().threadGroup 23 | } 24 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/utilities/extensions/FileExtensions.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.utilities.extensions 2 | 3 | import java.io.File 4 | 5 | fun File.isEmpty(): Boolean { 6 | return !this.exists() || this.length() == 0L 7 | } -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/utilities/extensions/NumberExtensions.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.utilities.extensions 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.Date 5 | 6 | fun Long.toDateString() : String = SimpleDateFormat("dd MMMM yyyy, HH:mm:ss").format(Date(this)) -------------------------------------------------------------------------------- /backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt: -------------------------------------------------------------------------------- 1 | package dev.dres.utilities.extensions 2 | 3 | import java.util.regex.Matcher 4 | 5 | /** 6 | * Converts a [String] to a [EvaluationId]. 7 | * 8 | * @return [EvaluationId] 9 | */ 10 | fun String.cleanPathString() = this.trim().replaceFirst("^~", Matcher.quoteReplacement(System.getProperty("user.home"))) 11 | 12 | /** 13 | * Converts a [String] to a [EvaluationId]. 14 | * 15 | * @return [EvaluationId] 16 | */ 17 | fun String.toPathParamKey() = "{$this}" -------------------------------------------------------------------------------- /backend/src/main/resources/dres-type-presets/10_Textual-Known-Item-Search.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Textual Known Item Search", 3 | "duration": 420, 4 | "targetOption": "SINGLE_MEDIA_SEGMENT", 5 | "hintOptions": [ 6 | "TEXT" 7 | ], 8 | "submissionOptions": [ 9 | "NO_DUPLICATES", 10 | "LIMIT_CORRECT_PER_TEAM", 11 | "TEMPORAL_SUBMISSION" 12 | ], 13 | "taskOptions": [ 14 | "HIDDEN_RESULTS" 15 | ], 16 | "scoreOption": "KIS", 17 | "configuration": { 18 | "LIMIT_CORRECT_PER_TEAM.limit": "1" 19 | } 20 | } -------------------------------------------------------------------------------- /backend/src/main/resources/dres-type-presets/20_Visual-Known-Item-Search.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Visual Known Item Search", 3 | "duration": 300, 4 | "targetOption": "SINGLE_MEDIA_SEGMENT", 5 | "hintOptions": [ 6 | "VIDEO_ITEM_SEGMENT" 7 | ], 8 | "submissionOptions": [ 9 | "NO_DUPLICATES", 10 | "LIMIT_CORRECT_PER_TEAM", 11 | "TEMPORAL_SUBMISSION" 12 | ], 13 | "taskOptions": [], 14 | "scoreOption": "KIS", 15 | "configuration": { 16 | "LIMIT_CORRECT_PER_TEAM.limit": "1" 17 | } 18 | } -------------------------------------------------------------------------------- /backend/src/main/resources/dres-type-presets/30_Ad-hoc-Video-Search.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ad-hoc Video Search", 3 | "duration": 300, 4 | "targetOption": "JUDGEMENT", 5 | "hintOptions": [ 6 | "TEXT" 7 | ], 8 | "submissionOptions": [ 9 | "NO_DUPLICATES", 10 | "TEMPORAL_SUBMISSION" 11 | ], 12 | "taskOptions": [ 13 | "MAP_TO_SEGMENT" 14 | ], 15 | "scoreOption": "AVS", 16 | "configuration": {} 17 | } -------------------------------------------------------------------------------- /backend/src/main/resources/dres-type-presets/40_LSC-Known-Item-Search.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LSC Known-Item Search", 3 | "duration": 300, 4 | "targetOption": "SINGLE_MEDIA_ITEM", 5 | "hintOptions": [ 6 | "TEXT" 7 | ], 8 | "submissionOptions": [ 9 | "NO_DUPLICATES", 10 | "LIMIT_CORRECT_PER_TEAM", 11 | "ITEM_SUBMISSION" 12 | ], 13 | "taskOptions": [ 14 | "HIDDEN_RESULTS" 15 | ], 16 | "scoreOption": "KIS", 17 | "configuration": { 18 | "LIMIT_CORRECT_PER_TEAM.limit": "1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/src/main/resources/dres-type-presets/50_LSC-Ad-hoc-Search.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LSC Ad-hoc Search", 3 | "duration": 180, 4 | "targetOption": "JUDGEMENT", 5 | "hintOptions": [ 6 | "TEXT" 7 | ], 8 | "submissionOptions": [ 9 | "NO_DUPLICATES", 10 | "ITEM_SUBMISSION" 11 | ], 12 | "taskOptions": [], 13 | "scoreOption": "AVS", 14 | "configuration": { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /backend/src/main/resources/dres-type-presets/60_LSC-Q&A-Text.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LSC Question & Answer Text", 3 | "duration": 300, 4 | "targetOption": "JUDGEMENT", 5 | "hintOptions": [ 6 | "TEXT" 7 | ], 8 | "submissionOptions": [ 9 | "NO_DUPLICATES", 10 | "LIMIT_CORRECT_PER_TEAM", 11 | "TEXTUAL_SUBMISSION" 12 | ], 13 | "taskOptions": [ 14 | "HIDDEN_RESULTS" 15 | ], 16 | "scoreOption": "KIS", 17 | "configuration": { 18 | "LIMIT_CORRECT_PER_TEAM.limit": "1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/src/main/resources/img/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/backend/src/main/resources/img/loading.png -------------------------------------------------------------------------------- /backend/src/main/resources/img/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/backend/src/main/resources/img/missing.png -------------------------------------------------------------------------------- /backend/src/main/resources/img/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/backend/src/main/resources/img/text.png -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataPath" : "./data", 3 | "externalPath": "./external", 4 | "enableSsl" : false 5 | } 6 | -------------------------------------------------------------------------------- /doc/dres_mode_of_operation_dist_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/doc/dres_mode_of_operation_dist_sync.png -------------------------------------------------------------------------------- /doc/dres_mode_of_operation_local_async.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/doc/dres_mode_of_operation_local_async.png -------------------------------------------------------------------------------- /doc/dres_mode_of_operation_local_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/doc/dres_mode_of_operation_local_sync.png -------------------------------------------------------------------------------- /doc/quit_tmux_session.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | session="DRES" 4 | 5 | tmux has-session -t $session 2>/dev/null 6 | 7 | if [ $? != 0 ]; then 8 | tmux send-keys -t $session ENTER "quit" ENTER 9 | sleep 1 10 | fi 11 | 12 | tmux has-session -t $session 2>/dev/null 13 | 14 | if [ $? != 0 ]; then 15 | tmux kill-session -t $session 16 | sleep 1 17 | fi -------------------------------------------------------------------------------- /doc/start_or_get_tmux_session.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | session="DRES" 4 | config="config.json" 5 | 6 | tmux has-session -t $session 2>/dev/null 7 | 8 | if [ $? != 0 ]; then 9 | tmux new-session -d -s $session "./backend $config" 10 | fi 11 | 12 | tmux attach -t $session 13 | -------------------------------------------------------------------------------- /doc/start_tmux_session.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | session="DRES" 4 | config="config.json" 5 | 6 | tmux has-session -t $session 2>/dev/null 7 | 8 | if [ $? != 0 ]; then 9 | tmux new-session -d -s $session "./backend $config" 10 | fi 11 | -------------------------------------------------------------------------------- /frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/yarn 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=yarn 3 | 4 | ### yarn ### 5 | # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored 6 | 7 | .yarn/* 8 | !.yarn/releases 9 | !.yarn/patches 10 | !.yarn/plugins 11 | !.yarn/sdks 12 | !.yarn/versions 13 | 14 | # if you are NOT using Zero-installs, then: 15 | # comment the following lines 16 | #!.yarn/cache 17 | 18 | # and uncomment the following lines 19 | .pnp.* 20 | 21 | # End of https://www.toptal.com/developers/gitignore/api/yarn 22 | -------------------------------------------------------------------------------- /frontend/.husky/common.sh: -------------------------------------------------------------------------------- 1 | command_exists () { 2 | command -v "$1" >/dev/null 2>&1 3 | } 4 | 5 | # Workaround for Windows 10, Git Bash and Yarn 6 | if command_exists winpty && test -t 1; then 7 | exec < /dev/tty 8 | fi 9 | -------------------------------------------------------------------------------- /frontend/.husky/post-checkout: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . "$(dirname "$0")/_/husky.sh" 4 | . "$(dirname "$0")/common.sh" 5 | 6 | # Inspired by https://stackoverflow.com/a/16853458 7 | 8 | CHANGED=`git diff $1 $2 --stat -- doc/oas.json | wc -l ` 9 | if [ $CHANGED -gt 0 ]; 10 | then 11 | echo "OpenApi specifications changed. Regenerating client bindings." 12 | ./gradlew openApiGenerate 13 | else 14 | echo "OpenApi specifications not changed." 15 | fi 16 | -------------------------------------------------------------------------------- /frontend/.husky/post-merge: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . "$(dirname "$0")/_/husky.sh" 4 | . "$(dirname "$0")/common.sh" 5 | 6 | # Inspired by https://stackoverflow.com/a/16853458 7 | 8 | CHANGED=`git diff HEAD#{1} --stat -- doc/oas.json | wc -l` 9 | if [ $CHANGED -gt 0 ]; 10 | then 11 | echo "OpenApi specifications changed. Regenerating client bindings" 12 | ./gradlew openApiGenerate 13 | else 14 | echo "No changes in OpenApi specifications" 15 | fi 16 | -------------------------------------------------------------------------------- /frontend/.husky/pre-commit: -------------------------------------------------------------------------------- 1 | ##!/bin/sh 2 | #. "$(dirname "$0")/_/husky.sh" 3 | #. "$(dirname "$0")/common.sh" 4 | #cd frontend 5 | # yarn lint-staged 6 | -------------------------------------------------------------------------------- /frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/yarn 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=yarn 3 | 4 | ### yarn ### 5 | # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored 6 | 7 | .yarn/* 8 | !.yarn/releases 9 | !.yarn/patches 10 | !.yarn/plugins 11 | !.yarn/sdks 12 | !.yarn/versions 13 | 14 | # if you are NOT using Zero-installs, then: 15 | # comment the following lines 16 | #!.yarn/cache 17 | 18 | # and uncomment the following lines 19 | .pnp.* 20 | 21 | # End of https://www.toptal.com/developers/gitignore/api/yarn 22 | 23 | node_modules/ 24 | .angular-cli.json 25 | .angular/ 26 | .gradle/ 27 | .husky/ 28 | .yarn/ 29 | build/ 30 | nodejs/ 31 | npm* 32 | openapi/ 33 | -------------------------------------------------------------------------------- /frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true, 5 | "printWidth": 128, 6 | "bracketSpacing": true, 7 | "semi": true, 8 | "trailingComma": "es5" 9 | } 10 | -------------------------------------------------------------------------------- /frontend/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-standard", 4 | "stylelint-config-html", 5 | "stylelint-config-prettier", 6 | "stylelint-config-standard-scss", 7 | "stylelint-config-prettier-scss" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /frontend/.yarnrc.yml: -------------------------------------------------------------------------------- 1 | yarnPath: .yarn/releases/yarn-3.2.0.cjs 2 | nodeLinker: node-modules 3 | -------------------------------------------------------------------------------- /frontend/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: ['./src/**/*.e2e-spec.ts'], 13 | capabilities: { 14 | browserName: 'chrome', 15 | }, 16 | directConnect: true, 17 | baseUrl: 'http://localhost:4200/', 18 | framework: 'jasmine', 19 | jasmineNodeOpts: { 20 | showColors: true, 21 | defaultTimeoutInterval: 30000, 22 | print: function () {}, 23 | }, 24 | onPrepare() { 25 | require('ts-node').register({ 26 | project: require('path').join(__dirname, './tsconfig.json'), 27 | }); 28 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /frontend/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('dres-frontend app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain( 20 | jasmine.objectContaining({ 21 | level: logging.Level.SEVERE, 22 | } as logging.Entry) 23 | ); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es2018", 7 | "types": ["jasmine", "jasminewd2", "node"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma'), 14 | ], 15 | client: { 16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/dres-frontend'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true, 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true, 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /frontend/openapitools.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", 3 | "spaces": 2, 4 | "generator-cli": { 5 | "version": "5.2.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/app.component.scss -------------------------------------------------------------------------------- /frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.html: -------------------------------------------------------------------------------- 1 |

{{ isEditing() ? 'Edit' : 'Create' }} Media Collection

2 |
3 |
4 |

5 | 6 | Name 7 | 8 | 9 |

10 |

11 | 12 | Description 13 | 14 | 15 |

16 |

17 | 18 | Base Path 19 | 20 | 21 |

22 |
23 |
24 |
25 | 26 | 27 |
28 | -------------------------------------------------------------------------------- /frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/collection/collection-list/collection-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/collection/collection-list/collection-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/collection/collection-viewer/collection-viewer.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/collection/collection-viewer/collection-viewer.component.scss -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Batch Target Add

2 |
3 | Targets 5 | 6 | 7 |
8 |
9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .validation-error { 2 | font-size: 10px; 3 | color: darkred; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder-task-dialog/require-match.ts: -------------------------------------------------------------------------------- 1 | import { AbstractControl } from '@angular/forms'; 2 | 3 | /** 4 | * https://onthecode.co.uk/force-selection-angular-material-autocomplete/ 5 | * @param control 6 | * @constructor 7 | */ 8 | export function RequireMatch(control: AbstractControl) { 9 | const selection: any = control.value; 10 | if (typeof selection === 'string') { 11 | return { incorrect: true }; 12 | } 13 | return null; 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
6 | -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.scss: -------------------------------------------------------------------------------- 1 | .video-player { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .checkbox-section { 2 | margin: 12px 0; 3 | } 4 | 5 | .checkbox-section mat-checkbox { 6 | margin: 0 12px; 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/app/competition/competition-builder/competition-builder.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/competition/competition-builder/competition-builder.component.scss -------------------------------------------------------------------------------- /frontend/src/app/error-handling/error-handling.module.ts: -------------------------------------------------------------------------------- 1 | import { ErrorHandler, NgModule } from "@angular/core"; 2 | import { CommonModule } from '@angular/common'; 3 | import { GlobalErrorHandlerService } from "./global-error-handler.service"; 4 | import { HTTP_INTERCEPTORS } from "@angular/common/http"; 5 | import { DresBackendUnauthorisedHandlerService } from "./dres-backend-unauthorised-handler.service"; 6 | 7 | 8 | 9 | @NgModule({ 10 | declarations: [], 11 | imports: [ 12 | CommonModule 13 | ], 14 | providers: [ 15 | {provide: ErrorHandler, useClass: GlobalErrorHandlerService}, 16 | 17 | ] 18 | }) 19 | export class ErrorHandlingModule { } 20 | -------------------------------------------------------------------------------- /frontend/src/app/error-handling/global-error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NgZone } from "@angular/core"; 2 | import { ErrorDialogService } from "../error/error-dialog.service"; 3 | import { HttpErrorResponse } from "@angular/common/http"; 4 | 5 | @Injectable() 6 | export class GlobalErrorHandlerService { 7 | 8 | constructor( 9 | private errorDialogService: ErrorDialogService, 10 | private zone: NgZone 11 | ) {} 12 | 13 | handleError(error: Error) { 14 | this.zone.run(() => 15 | this.errorDialogService.openDialog( 16 | error?.message 17 | ) 18 | ); 19 | 20 | console.error('Error from global error handler', error); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/app/error/error-dialog.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { MatDialog } from "@angular/material/dialog"; 3 | import { ErrorDialogComponent } from "./error-dialog/error-dialog.component"; 4 | 5 | @Injectable() 6 | export class ErrorDialogService { 7 | 8 | private opened = false; 9 | 10 | constructor(private dialog: MatDialog) { 11 | 12 | } 13 | 14 | openDialog(message: string):void { 15 | if(!this.opened){ 16 | this.opened = true; 17 | const dialogRef = this.dialog.open(ErrorDialogComponent, { 18 | data: {message}, 19 | maxHeight: '100%;', 20 | width: '800px', 21 | maxWidth: '100%', 22 | disableClose: true, 23 | hasBackdrop: true 24 | }); 25 | 26 | dialogRef.afterClosed().subscribe(() => this.opened = false); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/app/error/error-dialog/error-dialog.component.html: -------------------------------------------------------------------------------- 1 |

An error occurred!

2 |
3 |

4 | Something went wrong! See the browser console for the full stack trace. 5 |

6 | 7 | 8 | {{data?.message || 'FATAL: No error message present'}} 9 | 10 | 11 |
12 |
13 | 14 |
15 | -------------------------------------------------------------------------------- /frontend/src/app/error/error-dialog/error-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .error { 2 | font-family: monospace; 3 | word-break: break-all; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/error/error-dialog/error-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject } from "@angular/core"; 2 | import { MAT_DIALOG_DATA } from "@angular/material/dialog"; 3 | 4 | @Component({ 5 | selector: 'app-error-dialog', 6 | templateUrl: './error-dialog.component.html', 7 | styleUrls: ['./error-dialog.component.scss'] 8 | }) 9 | export class ErrorDialogComponent { 10 | constructor(@Inject(MAT_DIALOG_DATA) public data: {message: string}) { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/error/error.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from "@angular/core"; 2 | import {ForbiddenComponent} from "./forbidden.component"; 3 | import {NotFoundComponent} from "./not-found.component"; 4 | import {MatCardModule} from "@angular/material/card"; 5 | import { ErrorDialogComponent } from './error-dialog/error-dialog.component'; 6 | import { MatDialogModule } from "@angular/material/dialog"; 7 | import { SharedModule } from "../shared/shared.module"; 8 | import { ErrorDialogService } from "./error-dialog.service"; 9 | 10 | @NgModule({ 11 | declarations: [ 12 | ForbiddenComponent, 13 | NotFoundComponent, 14 | ErrorDialogComponent 15 | ], 16 | imports: [ 17 | MatCardModule, 18 | MatDialogModule, 19 | SharedModule 20 | ], 21 | exports: [], 22 | providers: [ErrorDialogService] 23 | }) 24 | export class ErrorModule { 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.scss: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/app/evaluation/admin/submission/answer/answer.component.scss: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.scss: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | } 4 | 5 | tr.submissions-detail-row { 6 | height: 0; 7 | } 8 | tr.submissions-element-row:not(.submissions-expanded-row):hover { 9 | background: rgba(255,255,255,0.1); 10 | } 11 | tr.submissions-element-row:not(.submissions-expanded-row):active { 12 | background: rgba(255,255,255,0.2); 13 | } 14 | 15 | .submissions-element-row td { 16 | border-bottom-width: 0; 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/evaluation/admin/submission/template-info/template-info.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.scss -------------------------------------------------------------------------------- /frontend/src/app/evaluation/admin/submission/template-info/template-info.component.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Component, Input } from "@angular/core"; 2 | import { ApiTarget, ApiTaskTemplate } from "../../../../../../openapi"; 3 | 4 | @Component({ 5 | selector: 'app-template-info', 6 | templateUrl: './template-info.component.html', 7 | styleUrls: ['./template-info.component.scss'] 8 | }) 9 | export class TemplateInfoComponent implements AfterViewInit{ 10 | 11 | public shownElement: ApiTarget; 12 | 13 | @Input() 14 | public template: ApiTaskTemplate; 15 | 16 | ngAfterViewInit(): void { 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/app/evaluation/task-controls/task-controls.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/evaluation/task-controls/task-controls.component.scss -------------------------------------------------------------------------------- /frontend/src/app/judgement/judgement-dialog/judgement-dialog-content.model.ts: -------------------------------------------------------------------------------- 1 | export interface JudgementDialogContent { 2 | title: string; 3 | body: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/judgement/judgement-dialog/judgement-dialog.component.html: -------------------------------------------------------------------------------- 1 |

{{ content.title }}

2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/app/judgement/judgement-dialog/judgement-dialog.component.scss: -------------------------------------------------------------------------------- 1 | .centered-button { 2 | margin: 0 auto; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/app/judgement/judgement-dialog/judgement-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject, OnInit } from '@angular/core'; 2 | import { MAT_DIALOG_DATA } from '@angular/material/dialog'; 3 | import { JudgementDialogContent } from './judgement-dialog-content.model'; 4 | 5 | @Component({ 6 | selector: 'app-judgement-welcome-dialog', 7 | templateUrl: './judgement-dialog.component.html', 8 | styleUrls: ['./judgement-dialog.component.scss'], 9 | }) 10 | export class JudgementDialogComponent implements OnInit { 11 | constructor(@Inject(MAT_DIALOG_DATA) public content: JudgementDialogContent) {} 12 | 13 | ngOnInit(): void {} 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/app/judgement/judgement-media-viewer.component.scss: -------------------------------------------------------------------------------- 1 | .video-player { 2 | width: 100%; 3 | max-height: 50vh; 4 | } 5 | 6 | .controls-container { 7 | padding-top: 0.5rem; 8 | } 9 | 10 | .temporalContext { 11 | border: solid red; 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/app/judgement/judgement-viewer.component.scss: -------------------------------------------------------------------------------- 1 | .controls-container, 2 | .header-container { 3 | padding-top: 1rem; 4 | } 5 | 6 | .message { 7 | padding-top: 1rem; 8 | padding-bottom: 1rem; 9 | } 10 | 11 | .button-wrapper { 12 | flex: 1 1 100%; 13 | } 14 | 15 | .button-wrapper button { 16 | width: 100px; 17 | height: 100px 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/app/judgement/judgement-voting-viewer.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

Vote for Submission

5 | 6 |
7 |
8 |

Description: {{ judgementRequest?.taskDescription }}

9 |
10 |
11 |
12 |
13 | 14 | 15 |
16 | 17 |
18 | 19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /frontend/src/app/judgement/judgement-voting-viewer.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/judgement/judgement-voting-viewer.component.scss -------------------------------------------------------------------------------- /frontend/src/app/model/access-checking.interface.ts: -------------------------------------------------------------------------------- 1 | import { UserGroup } from './user-group.model'; 2 | 3 | export interface AccessChecking { 4 | hasAccessFor(group: UserGroup): boolean; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/app/model/config.default.ts: -------------------------------------------------------------------------------- 1 | import { IConfig } from './config.interface'; 2 | 3 | export class DefaultConfig implements IConfig { 4 | private url = new URL(window.location.href); 5 | public readonly endpoint = { 6 | host: this.url.hostname, 7 | port: this.url.port === '' ? -1 : parseInt(this.url.port, 10), 8 | tls: this.url.protocol === 'https:', 9 | }; 10 | public readonly effects = { 11 | mute: false, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/model/config.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IConfig { 2 | endpoint: { host: string; port: number; tls: boolean }; 3 | effects: { mute: boolean }; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/model/user-group.model.ts: -------------------------------------------------------------------------------- 1 | import {ApiRole} from '../../../openapi'; 2 | 3 | export class UserGroup { 4 | constructor(public readonly name: string, public readonly roles: ApiRole[]) {} 5 | 6 | debugRoles(): string { 7 | return JSON.stringify(this.roles); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/app/model/ws/client-message-type.enum.ts: -------------------------------------------------------------------------------- 1 | export namespace ClientMessageType { 2 | export type ClientMessageTypeEnum = 'ACK' | 'REGISTER' | 'UNREGISTER' | 'PING'; 3 | export const ClientMessageTypeEnum = { 4 | ACK: 'ACK' as ClientMessageTypeEnum, 5 | REGISTER: 'REGISTER' as ClientMessageTypeEnum, 6 | UNREGISTER: 'UNREGISTER' as ClientMessageTypeEnum, 7 | PING: 'PING' as ClientMessageTypeEnum, 8 | }; 9 | export const ClientMessageTypes: ClientMessageTypeEnum[] = ['ACK', 'REGISTER', 'UNREGISTER', 'PING']; 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/app/model/ws/ws-client-message.interface.ts: -------------------------------------------------------------------------------- 1 | import { ClientMessageType } from './client-message-type.enum'; 2 | import ClientMessageTypeEnum = ClientMessageType.ClientMessageTypeEnum; 3 | import { IWsMessage } from './ws-message.interface'; 4 | 5 | /** 6 | * Structure of a message as generated by the DRES client. 7 | */ 8 | export interface IWsClientMessage extends IWsMessage { 9 | evaluationId: string; 10 | type: ClientMessageTypeEnum; 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/app/model/ws/ws-message.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Structure of a message as generated by the DRES client. 3 | */ 4 | import { ServerMessageType } from './server-message-type.enum'; 5 | import ServerMessageTypeEnum = ServerMessageType.ServerMessageTypeEnum; 6 | import { ClientMessageType } from './client-message-type.enum'; 7 | import ClientMessageTypeEnum = ClientMessageType.ClientMessageTypeEnum; 8 | 9 | export interface IWsMessage { 10 | evaluationId: string; 11 | type: ClientMessageTypeEnum | ServerMessageTypeEnum; 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/app/model/ws/ws-server-message.interface.ts: -------------------------------------------------------------------------------- 1 | import { ServerMessageType } from './server-message-type.enum'; 2 | import ServerMessageTypeEnum = ServerMessageType.ServerMessageTypeEnum; 3 | import { IWsMessage } from './ws-message.interface'; 4 | 5 | /** 6 | * Structure of a ServerMessage as generated by the DRES server. 7 | */ 8 | export interface IWsServerMessage extends IWsMessage { 9 | evaluationId: string; 10 | taskId?: string; 11 | type: ServerMessageTypeEnum; 12 | timestamp: number; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.scss -------------------------------------------------------------------------------- /frontend/src/app/run/run-admin-view.component.scss: -------------------------------------------------------------------------------- 1 | .grid { 2 | width: 100%; 3 | height: 90vh; 4 | } 5 | 6 | .header { 7 | width: 100%; 8 | margin-bottom: 10px; 9 | } 10 | 11 | .body { 12 | display: flex; 13 | width: 100%; 14 | margin-top: 10px; 15 | margin-bottom: 10px; 16 | } 17 | 18 | .footer { 19 | width: 100%; 20 | margin-top: 10px; 21 | } 22 | 23 | .left { 24 | width: 25%; 25 | margin-right: auto; 26 | } 27 | 28 | .main { 29 | width: 49%; 30 | margin-right: auto; 31 | margin-left: auto; 32 | } 33 | 34 | .right { 35 | width: 25%; 36 | margin-left: auto; 37 | } 38 | 39 | .alt-list-avatar { 40 | border-radius: 5% !important; 41 | } 42 | 43 | .outline-white { 44 | font-weight: bold; 45 | text-shadow: #fff 0px 0px 1px; 46 | -webkit-font-smoothing: antialiased; 47 | 48 | } 49 | 50 | .p4 { 51 | padding: 4px; 52 | } 53 | 54 | .image-fit-to-container { 55 | width: 100%; 56 | height: 100%; 57 | object-fit: contain; 58 | overflow: hidden; 59 | } 60 | -------------------------------------------------------------------------------- /frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.scss -------------------------------------------------------------------------------- /frontend/src/app/run/run-list.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Ongoing Evaluations

4 |
5 |
6 | 13 |
14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /frontend/src/app/run/run-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/run/run-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/run/score-history/run-score-history.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | Select scoreboard 5 | 6 | {{ scoreboard }} 7 | 8 | 9 |
10 |
11 |
12 | 23 | 24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /frontend/src/app/run/score-history/run-score-history.component.scss: -------------------------------------------------------------------------------- 1 | .grid { 2 | width: 100%; 3 | } 4 | 5 | .header { 6 | width: 100%; 7 | margin-bottom: 10px; 8 | } 9 | 10 | .body { 11 | display: flex; 12 | width: 100%; 13 | margin-top: 10px; 14 | margin-bottom: 10px; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/services/can-deactivate.guard.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot, UrlTree } from '@angular/router'; 3 | import { Injectable } from '@angular/core'; 4 | 5 | export interface DeactivationGuarded { 6 | canDeactivate(nextState?: RouterStateSnapshot): Observable | Promise | boolean; 7 | } 8 | 9 | @Injectable() 10 | export class CanDeactivateGuard implements CanDeactivate { 11 | canDeactivate( 12 | component: DeactivationGuarded, 13 | currentRoute: ActivatedRouteSnapshot, 14 | currentState: RouterStateSnapshot, 15 | nextState?: RouterStateSnapshot 16 | ): Observable | Promise | boolean | UrlTree { 17 | return component.canDeactivate ? component.canDeactivate(nextState) : true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/app/services/logging/log-entry.model.ts: -------------------------------------------------------------------------------- 1 | import { LogLevel } from "./log-level.enum"; 2 | 3 | export interface LogEntry { 4 | timestamp: Date 5 | level: LogLevel 6 | source: string 7 | message: string 8 | data?: any[] 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/app/services/logging/log-level.enum.ts: -------------------------------------------------------------------------------- 1 | export enum LogLevel { 2 | ALL, 3 | TRACE, 4 | DEBUG, 5 | INFO, 6 | WARN, 7 | ERROR, 8 | FATAL, 9 | OFF 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/app/services/logging/log-service.factory.ts: -------------------------------------------------------------------------------- 1 | import { LogService } from "./log.service"; 2 | import { LoggerConfig } from "./logger.config"; 3 | 4 | export const logServiceFactory = (config: LoggerConfig) => { 5 | return new LogService(config) 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/app/services/logging/log-service.provider.ts: -------------------------------------------------------------------------------- 1 | import { LogService } from "./log.service"; 2 | import { logServiceFactory } from "./log-service.factory"; 3 | import { LOGGER_CONFIG } from "./logger-config.token"; 4 | 5 | export const logServiceProvider = { 6 | provide: LogService, 7 | useFactory: logServiceFactory, 8 | deps: [LOGGER_CONFIG], 9 | }; 10 | -------------------------------------------------------------------------------- /frontend/src/app/services/logging/logger-config.token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from "@angular/core"; 2 | import { LoggerConfig } from "./logger.config"; 3 | 4 | export const LOGGER_CONFIG = new InjectionToken("__LOGGER_CONFIG__", { 5 | providedIn: "root", 6 | factory: () => { 7 | return {identifier: "Root"} as LoggerConfig 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /frontend/src/app/services/logging/logger.config.ts: -------------------------------------------------------------------------------- 1 | export interface LoggerConfig { 2 | identifier: string 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/app/services/logging/logging.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | import { LogService } from "./log.service"; 4 | import { LOGGER_CONFIG } from "./logger-config.token"; 5 | import { LoggerConfig } from "./logger.config"; 6 | import { logServiceProvider } from "./log-service.provider"; 7 | 8 | 9 | @NgModule({ 10 | declarations: [], 11 | imports: [ 12 | CommonModule 13 | ], providers: [ 14 | LogService, 15 | logServiceProvider 16 | ] 17 | }) 18 | export class LoggingModule { 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/app/services/navigation/back-button.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener, Input } from '@angular/core'; 2 | import { NavigationService } from './navigation.service'; 3 | 4 | @Directive({ 5 | selector: '[appBackButton]', 6 | }) 7 | export class BackButtonDirective { 8 | @Input() appBackButton = false; 9 | 10 | constructor(private navigation: NavigationService) {} 11 | 12 | @HostListener('click') 13 | onClick(): void { 14 | this.navigation.back(this.appBackButton); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/app/services/navigation/forward-button.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener } from '@angular/core'; 2 | import { NavigationService } from './navigation.service'; 3 | 4 | @Directive({ 5 | selector: '[appForwardButton]', 6 | }) 7 | export class ForwardButtonDirective { 8 | constructor(private navigation: NavigationService) {} 9 | 10 | @HostListener('click') 11 | onClick(): void { 12 | this.navigation.forward(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/enhance-task-past-info.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import {ApiTaskTemplateInfo} from '../../../../openapi'; 3 | 4 | @Pipe({ 5 | name: 'enhanceTaskPastInfo', 6 | }) 7 | export class EnhanceTaskPastInfoPipe implements PipeTransform { 8 | // FIXME compiler happiness: not sure whether this is appropriate 9 | transform(value: ApiTaskTemplateInfo, pastTasks: ApiTaskTemplateInfo[]): ApiTaskTemplateInfo | null { 10 | if (!value || !pastTasks) { 11 | return null; 12 | } 13 | const filtered = pastTasks.filter((pt) => pt.templateId === value.templateId); 14 | return filtered.length > 0 ? filtered[0] : null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/enhance-task-submission-info.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import {ApiSubmission, ApiSubmissionInfo, ApiTaskTemplateInfo} from '../../../../openapi'; 3 | 4 | @Pipe({ 5 | name: 'enhanceTaskSubmissionInfo', 6 | }) 7 | export class EnhanceTaskSubmissionInfoPipe implements PipeTransform { 8 | // FIXME compiler happiness: not sure whether this is appropriate 9 | transform(value: ApiTaskTemplateInfo, submissionInfos: ApiSubmissionInfo[]): ApiSubmissionInfo | null { 10 | if (!value || !submissionInfos) { 11 | return null; 12 | } 13 | const filtered = submissionInfos.filter((si) => si.taskId === value.templateId); 14 | return filtered.length > 0 ? filtered[0] : null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/epoch2date.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | /** 4 | * Converts unix timestamp to Date type 5 | */ 6 | @Pipe({ 7 | name: 'epoch2date', 8 | }) 9 | export class Epoch2DatePipePipe implements PipeTransform { 10 | transform(value: number): Date { 11 | return new Date(value); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/filter-not-in.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | /** 4 | * Simple pipe to filter an array such that those element not in the haytack are kept. 5 | */ 6 | @Pipe({ 7 | name: 'filterNotIn' 8 | }) 9 | export class FilterNotInPipe implements PipeTransform { 10 | 11 | /** 12 | * Filters a given array such that none of the elements in haystack are in the resulting array. 13 | * 14 | * @param value The array to filter 15 | * @param haystack The haytack to search in 16 | * @param propertyKey The key of the property to compare on. 17 | */ 18 | transform(value: any[], haystack: any[], propertyKey : string = null): any[] { 19 | if(value){ 20 | if(propertyKey){ 21 | return value.filter(it => !haystack.map(item => item != null && item[propertyKey]).includes(it[propertyKey])) 22 | } 23 | return value.filter(it => !haystack.includes(it)); 24 | }else{ 25 | return []; 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/format-media-item.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { ApiMediaItem } from "../../../../openapi"; 3 | 4 | export interface MediaItemDisplayOptions { 5 | showId?: boolean 6 | shortenId?: boolean 7 | showType?: boolean 8 | } 9 | 10 | @Pipe({ 11 | name: 'formatMediaItem' 12 | }) 13 | export class FormatMediaItemPipe implements PipeTransform { 14 | 15 | transform(value: ApiMediaItem, options?: MediaItemDisplayOptions): string { 16 | if(value){ 17 | let out = value.name 18 | if(options){ 19 | if(options.showId){ 20 | const end = options.shortenId ? 8 : value.mediaItemId.length 21 | out += ' ('+value.mediaItemId.substring(0,end)+')' 22 | } 23 | if(options.showType){ 24 | out += ' (' + value.type + ')' 25 | } 26 | } 27 | return out; 28 | } 29 | return '' 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/format-temporal-point.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { ApiTemporalPoint } from "../../../../openapi"; 3 | import { FormatTemporalUnitPipe } from "./format-temporal-unit.pipe"; 4 | import { FormatTimePipePipe } from "./format-time-pipe.pipe"; 5 | 6 | @Pipe({ 7 | name: 'formatTemporalPoint' 8 | }) 9 | export class FormatTemporalPointPipe implements PipeTransform { 10 | 11 | constructor( 12 | private unitPipe: FormatTemporalUnitPipe, 13 | private timePipe: FormatTimePipePipe, 14 | ) {} 15 | transform(value: ApiTemporalPoint, ...args: unknown[]): string { 16 | switch(value.unit){ 17 | case "FRAME_NUMBER": 18 | case "SECONDS": 19 | return `${value.value}${this.unitPipe.transform(value.unit)}`; 20 | case "MILLISECONDS": 21 | return `${Number(value.value) / 1000}${this.unitPipe.transform(value.unit)}`; 22 | case "TIMECODE": 23 | return this.timePipe.transform(Number(value.value)); 24 | 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/format-temporal-unit.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { ApiTemporalUnit } from "../../../../openapi"; 3 | 4 | @Pipe({ 5 | name: 'formatTemporalUnit' 6 | }) 7 | export class FormatTemporalUnitPipe implements PipeTransform { 8 | 9 | transform(value: ApiTemporalUnit, ...args: unknown[]): unknown { 10 | switch(value){ 11 | case "FRAME_NUMBER": 12 | return "f"; 13 | case "SECONDS": 14 | return "s"; 15 | case "MILLISECONDS": 16 | return "ms"; 17 | case "TIMECODE": 18 | return ""; 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/format-time-pipe.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | /** 4 | * Transforms a given number as hours:minutes:seconds 5 | */ 6 | @Pipe({ 7 | name: 'formatTime', 8 | }) 9 | export class FormatTimePipePipe implements PipeTransform { 10 | transform(value: number, inMs = true): string { 11 | const divisor = inMs ? 1000 : 1; 12 | const hrs = Math.floor((value/divisor) / 3600); 13 | const mins = Math.floor(((value/divisor) % 3600) / 60); 14 | const secs = Math.floor((value/divisor) % 60); 15 | const ms = inMs ? Math.floor(value % divisor) : ''; 16 | let out = ''; 17 | /* Hours if present */ 18 | if (hrs > 0) { 19 | out += '' + (''+hrs).padStart(2, '0') + ':'; 20 | } 21 | /* Minutes */ 22 | out += '' + (''+mins).padStart(2, '0') + ':'; 23 | /* Seconds */ 24 | out += '' + (''+secs).padStart(2, '0') + (inMs ? '.' : ''); 25 | /* Milliseconds */ 26 | if(inMs){ 27 | out += '' + (''+ms).padStart(3,'0'); 28 | } 29 | return out; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/not-in-list-filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'notInListFilter' 5 | }) 6 | export class NotInListFilterPipe implements PipeTransform { 7 | 8 | transform(list: any[], filter: any): any[] { 9 | if(!list){ 10 | return []; 11 | } 12 | if(!filter){ 13 | return list; 14 | } 15 | return list.filter(it => it != filter); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/order-by.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from "@angular/core"; 2 | 3 | @Pipe({ 4 | name: "orderBy" 5 | }) 6 | export class OrderByPipe implements PipeTransform { 7 | 8 | transform(value: any[], order = "", compare: (a: any, b: any) => number): any[] { 9 | if (!value || order === "" || !order) { 10 | return value; 11 | } 12 | if (value.length <= 1) { 13 | return value; 14 | } 15 | if (!compare) { 16 | if (order === "asc") { 17 | return value.sort(); 18 | } else { 19 | return value.sort().reverse(); 20 | } 21 | } else { 22 | return value.sort(compare); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/resolve-media-item-preview.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { AppConfig } from "../../app.config"; 3 | import { ApiMediaItem } from "../../../../openapi"; 4 | import { TimeUtilities } from "../../utilities/time.utilities"; 5 | import { isMediaItemUrlOptions } from "./resolve-media-item-url.pipe"; 6 | 7 | @Pipe({ 8 | name: 'resolveMediaItemPreview' 9 | }) 10 | export class ResolveMediaItemPreviewPipe implements PipeTransform { 11 | 12 | constructor(private config: AppConfig){ 13 | 14 | } 15 | 16 | transform(value: ApiMediaItem, ...args: unknown[]): string { 17 | let suffix = ''; 18 | if(args && args.length > 0){ 19 | if(args[0] && isMediaItemUrlOptions(args[0]) && args[0].time){ 20 | return this.config.resolveImagePreviewUrl(value.mediaItemId, `${args[0].time}`) 21 | } 22 | } 23 | return this.config.resolveImagePreviewUrl(value.mediaItemId) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/resolve-mediaitem.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { ApiMediaItem, CollectionService } from "../../../../openapi"; 3 | import { Observable } from "rxjs"; 4 | 5 | @Pipe({ 6 | name: 'resolveMediaItem' 7 | }) 8 | export class ResolveMediaItemPipe implements PipeTransform { 9 | 10 | private cachedItem: Observable | null = null 11 | private cachedId: string = '' 12 | 13 | constructor( 14 | private mediaService: CollectionService, 15 | ){} 16 | 17 | transform(value: string, ...args: unknown[]): Observable { 18 | if(value !== this.cachedId){ 19 | this.cachedItem = null; 20 | this.cachedId = value; 21 | this.cachedItem = this.mediaService.getApiV2MediaItemByMediaItemId(value); 22 | } 23 | console.log(this.cachedItem) 24 | return this.cachedItem; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/resolve-team.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import {ApiTeam, ApiTeamInfo} from '../../../../openapi'; 3 | 4 | @Pipe({ 5 | name: 'resolveTeam', 6 | }) 7 | export class ResolveTeamPipe implements PipeTransform { 8 | transform(teamId: string, teams: ApiTeamInfo[]): ApiTeamInfo | null { 9 | if (!teamId || !teams) { 10 | return null; 11 | } 12 | const filtered = teams.filter((t) => t.id === teamId); 13 | return filtered.length > 0 ? filtered[0] : null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/round-pipe.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | /** 4 | * Rounds the given number to an integer. 5 | * 6 | * Usage: 7 | * value | round 8 | * 9 | * Example 1: 10 | * {{ 2.45432 | round }} 11 | * formats to 2 12 | * 13 | * Example 2: 14 | * {{ 4.6873 | round }} 15 | * formats to 5 16 | */ 17 | @Pipe({ 18 | name: 'round', 19 | }) 20 | export class RoundPipePipe implements PipeTransform { 21 | transform(value: number): number { 22 | return Math.round(value); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/space-to-newline.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'spaceToNewline' 5 | }) 6 | export class SpaceToNewlinePipe implements PipeTransform { 7 | 8 | transform(value: string, lineBreak= false): string { 9 | const nl: string = lineBreak ? '
' : '\n'; 10 | return value?.split(' ').join(nl) || ''; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/services/pipes/underscore-wordbreak.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'underscoreWordBreak' 5 | }) 6 | export class UnderscoreWordBreakPipe implements PipeTransform { 7 | 8 | /** 9 | * Simple pipe that adds the opportunity for the browser to break long words with underscores 10 | */ 11 | transform(value: any): any{ 12 | if(value){ 13 | if(typeof value === 'string'){ 14 | const doc = new DOMParser().parseFromString(value.replace(/_/g, "_​"), 'text/html'); 15 | return doc.documentElement.textContent 16 | }else{ 17 | return value 18 | } 19 | } 20 | return ''; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/api-status/api-status.component.html: -------------------------------------------------------------------------------- 1 |
2 | {{ (rtt | async) > 0 ? 'sensors' : 'sensors_off' }} 3 |   4 | {{ rtt | async }}ms 7 |
8 | -------------------------------------------------------------------------------- /frontend/src/app/shared/api-status/api-status.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/api-status/api-status.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/back-button/back-button.component.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /frontend/src/app/shared/back-button/back-button.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | align-self: center; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/app/shared/back-button/back-button.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-back-button', 5 | templateUrl: './back-button.component.html', 6 | styleUrls: ['./back-button.component.scss'], 7 | }) 8 | export class BackButtonComponent implements OnInit { 9 | @Input() forceBack = false; 10 | 11 | constructor() {} 12 | 13 | ngOnInit(): void {} 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/app/shared/confirmation-dialog/confirmation-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Confirmation Required

2 |
3 | {{ data.text }} 4 |
5 |
6 | 7 | 8 |
9 | -------------------------------------------------------------------------------- /frontend/src/app/shared/confirmation-dialog/confirmation-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/confirmation-dialog/confirmation-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/confirmation-dialog/confirmation-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject, OnInit } from '@angular/core'; 2 | import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; 3 | 4 | export interface ConfirmationDialogComponentData { 5 | text?: string; 6 | color?: string; 7 | } 8 | 9 | @Component({ 10 | selector: 'app-confirmation-dialog', 11 | templateUrl: './confirmation-dialog.component.html', 12 | styleUrls: ['./confirmation-dialog.component.scss'], 13 | }) 14 | export class ConfirmationDialogComponent { 15 | 16 | color = this.data?.color || 'warn'; 17 | 18 | constructor(public dialog: MatDialog, @Inject(MAT_DIALOG_DATA) public data: ConfirmationDialogComponentData) {} 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/app/shared/download-button/download-button.component.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /frontend/src/app/shared/download-button/download-button.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/download-button/download-button.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/dynamic-table/dynamic-table.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 |
{{column.header}} 5 | {{element[column.property]}} 6 |
11 | -------------------------------------------------------------------------------- /frontend/src/app/shared/dynamic-table/dynamic-table.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/dynamic-table/dynamic-table.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/dynamic-table/dynamic-table.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input} from '@angular/core'; 2 | 3 | export interface ColumnDefinition { 4 | property: string, 5 | type: string, // TODO enumize it 6 | header: string 7 | } 8 | 9 | @Component({ 10 | selector: 'app-dynamic-table', 11 | templateUrl: './dynamic-table.component.html', 12 | styleUrls: ['./dynamic-table.component.scss'] 13 | }) 14 | export class DynamicTableComponent { 15 | 16 | @Input() 17 | public dataSource: Array; 18 | 19 | @Input() 20 | public columnSchema: ColumnDefinition[]; 21 | 22 | @Input() 23 | public displayedColumns: string[]; 24 | 25 | constructor() { } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/app/shared/information-dialog/information-dialog.component.html: -------------------------------------------------------------------------------- 1 |

{{ title}}

2 |
3 | {{ text }} 4 |
5 |
6 | 7 |
8 | -------------------------------------------------------------------------------- /frontend/src/app/shared/information-dialog/information-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/information-dialog/information-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/information-dialog/information-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject } from "@angular/core"; 2 | import { MAT_DIALOG_DATA, MatDialog } from "@angular/material/dialog"; 3 | import { ConfirmationDialogComponentData } from "../confirmation-dialog/confirmation-dialog.component"; 4 | 5 | export interface InformationDialogComponentData { 6 | title?: string, 7 | text?: string, 8 | closeLbl?: string, 9 | color?: 'primary' | 'warn' | 'accent' | null 10 | } 11 | 12 | @Component({ 13 | selector: 'app-information-dialog', 14 | templateUrl: './information-dialog.component.html', 15 | styleUrls: ['./information-dialog.component.scss'] 16 | }) 17 | export class InformationDialogComponent { 18 | 19 | color = this.data?.color || 'primary'; 20 | text = this.data?.text || ''; 21 | title= this.data?.title || 'Information' 22 | closeLbl = this.data?.closeLbl || 'Ok' 23 | 24 | constructor(public dialog: MatDialog, @Inject(MAT_DIALOG_DATA) public data: InformationDialogComponentData) {} 25 | 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/app/shared/media-item-viewer/media-item-viewer.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { ApiMediaItem, ApiTemporalRange } from "../../../../openapi"; 3 | import { AppConfig } from "../../app.config"; 4 | import { TimeUtilities } from "../../utilities/time.utilities"; 5 | 6 | @Component({ 7 | selector: 'app-media-item-viewer', 8 | templateUrl: './media-item-viewer.component.html', 9 | styleUrls: ['./media-item-viewer.component.scss'] 10 | }) 11 | export class MediaItemViewerComponent { 12 | 13 | @Input() 14 | public toggleable: boolean = false; 15 | 16 | @Input() 17 | public showing: boolean = true; 18 | 19 | @Input() 20 | public displayNameAndId: boolean = true; 21 | 22 | @Input() 23 | public item: ApiMediaItem; 24 | 25 | @Input() 26 | public range?: ApiTemporalRange; 27 | 28 | isRangeSingular():boolean { 29 | return this.range && TimeUtilities.temporalPointEquals(this.range.start, this.range.end) 30 | } 31 | 32 | time():number{ 33 | const t = TimeUtilities.point2Milliseconds(this.range.start, this.item.fps) 34 | return t; 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/app/shared/search-box/search-box.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | search 9 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/src/app/shared/search-box/search-box.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/search-box/search-box.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/search-box/search-box.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, OnInit, Output } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: 'app-search-box', 5 | templateUrl: './search-box.component.html', 6 | styleUrls: ['./search-box.component.scss'] 7 | }) 8 | export class SearchBoxComponent{ 9 | // Source: https://angular-htpgvx.stackblitz.io 10 | @Output() filterChanged = new EventEmitter(); 11 | public searchBoxActive = false; 12 | filter: string; 13 | 14 | onFilterClear(){ 15 | this.filter = ''; 16 | this.searchBoxActive = false; 17 | this.filterChanged.emit(this.filter); 18 | } 19 | 20 | onTextChanged(){ 21 | this.filterChanged.emit(this.filter); 22 | } 23 | 24 | public clear(){ 25 | this.onFilterClear(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/app/shared/server-info/server-info.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/server-info/server-info.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/target-media-viewer/target-media-viewer.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
{{target.target}}
4 |
Judgement targets do not have a preview
5 |
Judgement targets do not have a preview
6 | 7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /frontend/src/app/shared/target-media-viewer/target-media-viewer.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.scss -------------------------------------------------------------------------------- /frontend/src/app/shared/target-media-viewer/target-media-viewer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { ApiTarget } from "../../../../openapi"; 3 | 4 | @Component({ 5 | selector: 'app-target-media-viewer', 6 | templateUrl: './target-media-viewer.component.html', 7 | styleUrls: ['./target-media-viewer.component.scss'] 8 | }) 9 | export class TargetMediaViewerComponent { 10 | 11 | 12 | @Input() 13 | public target: ApiTarget 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/app/shared/upload-json-button/upload-json-button.component.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/src/app/shared/upload-json-button/upload-json-button.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/shared/upload-json-button/upload-json-button.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/batch-add-target-dialog/batch-add-target-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Batch Add Targets

2 |
3 |

4 | Batch-add targets by file or by your input, each on its own line. 5 | Please not that the target name must be exactly matching the media item's name. 6 |

7 |
8 | File 9 | 10 | 11 |
12 | 13 | Targets 14 | 16 | 17 |
18 |
19 | 20 | 21 |
22 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/batch-add-target-dialog/batch-add-target-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/batch-add-target-dialog/batch-add-target-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/judges-list/judges-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.html: -------------------------------------------------------------------------------- 1 | This component shouldn't be used for rendering 2 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { UntypedFormControl } from "@angular/forms"; 3 | import { 4 | CompetitionFormBuilder 5 | } from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; 6 | import { AppConfig } from "../../../../app.config"; 7 | 8 | @Component({ 9 | selector: 'app-query-description-external-form-field', 10 | templateUrl: './query-description-external-form-field.component.html', 11 | styleUrls: ['./query-description-external-form-field.component.scss'] 12 | }) 13 | export class QueryDescriptionExternalFormFieldComponent { 14 | @Input() 15 | pathControl: UntypedFormControl 16 | @Input() 17 | formBuilder: CompetitionFormBuilder 18 | @Input() 19 | index: number 20 | 21 | showing: boolean = false; 22 | 23 | constructor(public config: AppConfig) { 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { 3 | QueryDescriptionExternalFormFieldComponent 4 | } from "../query-description-external-form-field/query-description-external-form-field.component"; 5 | import { 6 | CompetitionFormBuilder 7 | } from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; 8 | import { AppConfig } from "../../../../app.config"; 9 | 10 | @Component({ 11 | selector: 'app-query-description-external-image-form-field', 12 | templateUrl: './query-description-external-image-form-field.component.html', 13 | styleUrls: ['./query-description-external-image-form-field.component.scss'] 14 | }) 15 | export class QueryDescriptionExternalImageFormFieldComponent extends QueryDescriptionExternalFormFieldComponent{ 16 | 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.html: -------------------------------------------------------------------------------- 1 | This component should not be used for rendering 2 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Injector, Input } from "@angular/core"; 2 | import { 3 | QueryDescriptionMediaItemFormFieldComponent 4 | } from "../query-description-media-item-form-field/query-description-media-item-form-field.component"; 5 | 6 | @Component({ 7 | selector: 'app-query-description-media-item-image-form-field', 8 | templateUrl: './query-description-media-item-image-form-field.component.html', 9 | styleUrls: ['./query-description-media-item-image-form-field.component.scss'] 10 | }) 11 | export class QueryDescriptionMediaItemImageFormFieldComponent extends QueryDescriptionMediaItemFormFieldComponent{ 12 | 13 | constructor(injector: Injector) { 14 | super(injector) 15 | } 16 | 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { UntypedFormControl } from "@angular/forms"; 3 | 4 | @Component({ 5 | selector: 'app-query-description-text-form-field', 6 | templateUrl: './query-description-text-form-field.component.html', 7 | styleUrls: ['./query-description-text-form-field.component.scss'] 8 | }) 9 | export class QueryDescriptionTextFormFieldComponent { 10 | 11 | @Input() 12 | control: UntypedFormControl 13 | 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html: -------------------------------------------------------------------------------- 1 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.scss: -------------------------------------------------------------------------------- 1 | .validation-error { 2 | font-size: 10px; 3 | color: darkred; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.scss: -------------------------------------------------------------------------------- 1 | ul { 2 | padding-inline-start: 16px; /* 16px seems to line up bullet point with cell begin */ 3 | } 4 | 5 | .mat-column-submissions { 6 | width: 17%; /* those are the longest cells, hence manually setting the width */ 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss: -------------------------------------------------------------------------------- 1 | .mat-column-name { 2 | width: 15%; 3 | } 4 | 5 | .mat-column-group { 6 | width: 15%; 7 | } 8 | 9 | .mat-column-actions{ 10 | width: 25%; 11 | } 12 | 13 | .mat-mdc-row:hover { 14 | background: rgba(255,255,255,0.1); 15 | } 16 | 17 | .dres-selected-row { 18 | background: rgba(255,255,255,0.2); 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/team-builder-dialog/user-list-filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { ApiUser } from "../../../../../../openapi"; 3 | 4 | /** 5 | * A pipe enabling the filtering of a list of [ApiUser]s based on their username. 6 | */ 7 | @Pipe({ 8 | name: 'userListFilter' 9 | }) 10 | export class UserListFilterPipe implements PipeTransform { 11 | 12 | transform(list: ApiUser[], filter: string): ApiUser[] { 13 | if(!list){ 14 | // Catch no / empty list; 15 | return []; 16 | } 17 | if(!filter){ 18 | // Catch no / empty filter provided 19 | return list; 20 | }else{ 21 | return list.filter(user => { 22 | return user.username.toLowerCase().includes(filter.toLowerCase()) 23 | }); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/team-builder-dialog/user-list-in-other-filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { ApiUser } from "../../../../../../openapi"; 3 | 4 | /** 5 | * Simple filter which filters a list of [ApiUser]s based on whether they are present in another list. 6 | */ 7 | @Pipe({ 8 | name: 'userListInOtherFilter' 9 | }) 10 | export class UserListInOtherFilterPipe implements PipeTransform { 11 | 12 | transform(list: ApiUser[], other: ApiUser[]): ApiUser[] { 13 | if(!list){ 14 | return []; 15 | } 16 | if(!other){ 17 | return list; 18 | } 19 | return list.filter(it => !other.includes(it)) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/teamgroups-dialog/teamgroups-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/teamgroups-dialog/teamgroups-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/teams-list/teams-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Import {{title()}}

2 |
3 | 4 | 5 |
6 |
7 | 8 | 9 |
10 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{node.label}} 7 | 8 | 9 | 10 | 16 | {{node.label}} 19 | 20 | 21 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/template-information/template-information.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/template-information/template-information.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-builder/components/viewers-list/viewers-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-builder/components/viewers-list/viewers-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-create-dialog/template-create-dialog.component.html: -------------------------------------------------------------------------------- 1 |

New evaluation template

2 |
3 |
4 |

5 | 6 | Name 7 | 8 | 9 |

10 |

11 | 12 | Description 13 | 14 | 15 |

16 |
17 |
18 |
19 | 20 | 21 |
22 | -------------------------------------------------------------------------------- /frontend/src/app/template/template-create-dialog/template-create-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-create-dialog/template-create-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/template/template-list/template-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/template/template-list/template-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.scss -------------------------------------------------------------------------------- /frontend/src/app/user/admin-user-list/admin-user-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/user/admin-user-list/admin-user-list.component.scss -------------------------------------------------------------------------------- /frontend/src/app/user/login/login.component.html: -------------------------------------------------------------------------------- 1 | 2 | Login 3 | 4 |
5 |

6 | 7 | Username: 8 | 9 | 10 |

11 | 12 |

13 | 14 | Password: 15 | 16 | 17 |

18 | 19 |
20 | 21 |
22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /frontend/src/app/user/login/login.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | justify-content: center; 4 | margin: 100px 0px; 5 | } 6 | 7 | .mat-mdc-form-field { 8 | width: 100%; 9 | min-width: 300px; 10 | } 11 | 12 | /* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version. */ 13 | mat-card-title, 14 | mat-card-content { 15 | display: flex; 16 | justify-content: center; 17 | } 18 | 19 | .error { 20 | padding: 16px; 21 | width: 300px; 22 | color: white; 23 | background-color: red; 24 | } 25 | 26 | .button { 27 | display: flex; 28 | justify-content: flex-end; 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/app/user/profile/profile.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | justify-content: center; 4 | margin: 100px 0px; 5 | } 6 | 7 | mat-grid-list { 8 | min-width: 300px; 9 | } 10 | 11 | .error { 12 | padding: 16px; 13 | width: 300px; 14 | color: white; 15 | background-color: red; 16 | } 17 | 18 | .button { 19 | display: flex; 20 | justify-content: flex-end; 21 | } 22 | 23 | 24 | .flush-right { 25 | margin-left: auto; 26 | } 27 | 28 | form { 29 | padding: 5%; 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/app/utilities/api.utilities.ts: -------------------------------------------------------------------------------- 1 | import { ApiEvaluation, ApiEvaluationInfo, ApiEvaluationOverview, ApiEvaluationTemplate, DownloadService } from "../../../openapi"; 2 | 3 | /** 4 | * Type guard for ApiEvaluationTemplate. 5 | * Ignores existence of created and modified administrative fields and checks only for functional properties 6 | * @param obj 7 | */ 8 | export function instanceOfTemplate(obj: any): obj is ApiEvaluationTemplate{ 9 | const idCheck = 'templateId' in obj || 'id' in obj 10 | const minimumPropsCheck = 'name' in obj && 'description' in obj && 'taskTypes' in obj && 'taskGroups' in obj && 'tasks' in obj && 'teams' in obj && 'judges' in obj && 'teamGroups' in obj 11 | return idCheck && minimumPropsCheck 12 | } 13 | 14 | export function instanceOfEvaluation(obj: any): obj is ApiEvaluation{ 15 | const idCheck = 'evaluationId' in obj || 'id' in obj 16 | const minimumPropsCheck = 'name' in obj && 'type' in obj && 'template' in obj && 'created' in obj && 'tasks' in obj 17 | return idCheck && minimumPropsCheck 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/app/utilities/data.utilities.ts: -------------------------------------------------------------------------------- 1 | export class DataUtilities { 2 | /** 3 | * Converts a Base64 encoded string into an object URL of a Blob. 4 | * 5 | * @param base64 The base64 encoded string. 6 | * @param contentType The content type of the data. 7 | */ 8 | static base64ToUrl(base64: string, contentType: string): string { 9 | const binary = atob(base64); 10 | const byteNumbers = new Array(binary.length); 11 | for (let i = 0; i < binary.length; i++) { 12 | byteNumbers[i] = binary.charCodeAt(i); 13 | } 14 | const byteArray = new Uint8Array(byteNumbers); 15 | const blob = new Blob([byteArray], { type: contentType }); 16 | return window.URL.createObjectURL(blob); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/app/viewer/model/run-viewer-position.ts: -------------------------------------------------------------------------------- 1 | export enum Position { 2 | LEFT, 3 | RIGHT, 4 | CENTER, 5 | BOTTOM, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/app/viewer/query-object-preview/query-object-preview.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { VideoObjectPreviewComponent } from './video-object-preview.component'; 3 | import { TextObjectPreviewComponent } from './text-object-preview.component'; 4 | import { CommonModule } from '@angular/common'; 5 | import { ImageObjectPreviewComponent } from './image-object-preview.component'; 6 | 7 | @NgModule({ 8 | imports: [CommonModule], 9 | exports: [VideoObjectPreviewComponent, TextObjectPreviewComponent, ImageObjectPreviewComponent], 10 | declarations: [VideoObjectPreviewComponent, TextObjectPreviewComponent, ImageObjectPreviewComponent], 11 | providers: [], 12 | }) 13 | export class QueryObjectPreviewModule {} 14 | -------------------------------------------------------------------------------- /frontend/src/app/viewer/query-object-preview/query-object.interface.ts: -------------------------------------------------------------------------------- 1 | import { SafeUrl } from '@angular/platform-browser'; 2 | 3 | export type QueryObjectType = 'video' | 'text' | 'image'; 4 | export interface QueryObject { 5 | type: QueryObjectType; 6 | url?: SafeUrl; 7 | text?: string[]; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/app/viewer/run-viewer.component.scss: -------------------------------------------------------------------------------- 1 | .grid { 2 | width: 100%; 3 | height: 90vh; 4 | } 5 | 6 | .header { 7 | width: 100%; 8 | margin-bottom: 10px; 9 | } 10 | 11 | .body { 12 | display: flex; 13 | width: 100%; 14 | margin-top: 10px; 15 | margin-bottom: 10px; 16 | } 17 | 18 | .footer { 19 | width: 100%; 20 | margin-top: 10px; 21 | } 22 | 23 | .left { 24 | margin-right: auto; 25 | } 26 | 27 | .main { 28 | margin-right: auto; 29 | margin-left: auto; 30 | } 31 | 32 | .right { 33 | margin-left: auto; 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Normalized Competition Scores 5 | Scores of {{ currentTaskGroup | async }} 6 | 7 | 8 | 9 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.scss -------------------------------------------------------------------------------- /frontend/src/app/viewer/task-viewer.component.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | text-align: center; 3 | } 4 | 5 | .task-preview { 6 | padding: 25px; 7 | min-height: 30vh; 8 | background-color: black; 9 | } 10 | 11 | p.countdown { 12 | font-size: 2.5em; 13 | line-height: 2.5em; 14 | text-align: center; 15 | } 16 | 17 | p.preview-text { 18 | font-size: 2.5em; 19 | text-align: center; 20 | vertical-align: middle; 21 | display: table-cell; 22 | } 23 | 24 | .task-preview { 25 | -webkit-user-select: none; 26 | -khtml-user-select: none; 27 | -moz-user-select: none; 28 | -ms-user-select: none; 29 | -o-user-select: none; 30 | user-select: none; 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "endpoint": { 3 | "host": "localhost", 4 | "port": 8080, 5 | "tls": false 6 | }, 7 | "effects": { 8 | "mute": false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /frontend/src/immutable/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/android-icon-192x192.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /frontend/src/immutable/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/applause.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/applause.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/beep_1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/beep_1.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/beep_2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/beep_2.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/correct.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/correct.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/ding.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/ding.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/glass.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/glass.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/sad_trombone.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/sad_trombone.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/whistle.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/whistle.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/audio/wrong.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/audio/wrong.ogg -------------------------------------------------------------------------------- /frontend/src/immutable/assets/images/load-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/images/load-icon.png -------------------------------------------------------------------------------- /frontend/src/immutable/assets/images/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/images/missing.png -------------------------------------------------------------------------------- /frontend/src/immutable/assets/images/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/assets/images/text.png -------------------------------------------------------------------------------- /frontend/src/immutable/favicon-16x16.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/favicon-16x16.ico -------------------------------------------------------------------------------- /frontend/src/immutable/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/favicon-16x16.png -------------------------------------------------------------------------------- /frontend/src/immutable/favicon-32x32.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/favicon-32x32.ico -------------------------------------------------------------------------------- /frontend/src/immutable/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/favicon-32x32.png -------------------------------------------------------------------------------- /frontend/src/immutable/favicon-96x96.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/favicon-96x96.ico -------------------------------------------------------------------------------- /frontend/src/immutable/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/favicon-96x96.png -------------------------------------------------------------------------------- /frontend/src/immutable/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/favicon.ico -------------------------------------------------------------------------------- /frontend/src/immutable/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/mstile-150x150.png -------------------------------------------------------------------------------- /frontend/src/immutable/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/mstile-310x310.png -------------------------------------------------------------------------------- /frontend/src/immutable/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/frontend/src/immutable/mstile-70x70.png -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch((err) => console.error(err)); 14 | -------------------------------------------------------------------------------- /frontend/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 6 | 7 | // First, initialize the Angular testing environment. 8 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 9 | -------------------------------------------------------------------------------- /frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": ["src/main.ts", "src/polyfills.ts"], 8 | "include": ["src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "es2020", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "ES2022", 14 | "lib": [ 15 | "es2020", 16 | "dom" 17 | ], 18 | "useDefineForClassFields": false 19 | }, 20 | "angularCompilerOptions": { 21 | "fullTemplateTypeCheck": true, 22 | "strictInjectionParameters": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["src/test.ts", "src/polyfills.ts"], 8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # Specifies the JVM arguments used for the daemon process. 4 | # The setting is particularly useful for tweaking memory settings. 5 | org.gradle.jvmargs=-Xms64m -Xmx512m 6 | 7 | systemProp.https.protocols=TLSv1.1,TLSv1.2,TLSv1.3 8 | 9 | # Dependency versions 10 | version_bcrypt=0.4 11 | version_clikt=4.1.0 12 | version_fastutil=8.5.12 13 | version_fuel=2.3.1 14 | version_jaffree=2022.06.03 15 | version_javalin=6.1.6 16 | version_javalinopenapi=6.1.6 17 | version_javalinssl=6.1.6 18 | version_jline3=3.22.0 19 | version_junit=5.9.1 20 | version_kotlin=1.8.22 21 | version_kotlin_csv=1.9.1 22 | version_log4j=2.20.0 23 | version_picnic=0.6.0 24 | version_xodus=2.0.1 25 | version_xodus_dnq=2.0.0 26 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dres-dev/DRES/7fbe29f3d77f105fc6e1ae31c76bac7fd39a6bae/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'dres' 2 | include 'backend' 3 | include 'frontend' --------------------------------------------------------------------------------